This R script is used to validate the points in the ReSurvey database using RS indicators (indices + phenology + canopy height).

Load libraries

library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ──────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(here)
G3;here() starts at C:/Users/jimenezalfaro/OneDrive - Universidad de Oviedo/IMIB/Analyses/MOTIVATE/MOTIVATE_validation
g
library(gridExtra)
G3;
Adjuntando el paquete: ‘gridExtra’

gG3;The following object is masked from ‘package:dplyr’:

    combine

g
library(readxl)
library(scales)
G3;
Adjuntando el paquete: ‘scales’

gG3;The following object is masked from ‘package:purrr’:

    discard

gG3;The following object is masked from ‘package:readr’:

    col_factor

g
library(sf)
G3;Linking to GEOS 3.13.1, GDAL 3.10.2, PROJ 9.5.1; sf_use_s2() is TRUE
g
library(rnaturalearth)
library(dtplyr)
G3;Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
g
library(lme4)
G3;Cargando paquete requerido: Matrix
gG3;
Adjuntando el paquete: ‘Matrix’

gG3;The following objects are masked from ‘package:tidyr’:

    expand, pack, unpack

g
library(lmerTest)
G3;
Adjuntando el paquete: ‘lmerTest’

gG3;The following object is masked from ‘package:lme4’:

    lmer

gG3;The following object is masked from ‘package:stats’:

    step

g
library(car)
G3;Cargando paquete requerido: carData
gG3;
Adjuntando el paquete: ‘car’

gG3;The following object is masked from ‘package:dplyr’:

    recode

gG3;The following object is masked from ‘package:purrr’:

    some

g
library(ggeffects)
library(party)
G3;Cargando paquete requerido: grid
gG3;Cargando paquete requerido: mvtnorm
gG3;Cargando paquete requerido: modeltools
gG3;Cargando paquete requerido: stats4
gG3;
Adjuntando el paquete: ‘modeltools’

gG3;The following object is masked from ‘package:car’:

    Predict

gG3;The following object is masked from ‘package:lme4’:

    refit

gG3;Cargando paquete requerido: strucchange
gG3;Cargando paquete requerido: zoo
gG3;
Adjuntando el paquete: ‘zoo’

gG3;The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric

gG3;Cargando paquete requerido: sandwich
gG3;
Adjuntando el paquete: ‘strucchange’

gG3;The following object is masked from ‘package:stringr’:

    boundary

gG3;
Adjuntando el paquete: ‘party’

gG3;The following object is masked from ‘package:dplyr’:

    where

g
library(partykit)
G3;Cargando paquete requerido: libcoin
gG3;
Adjuntando el paquete: ‘partykit’

gG3;The following objects are masked from ‘package:party’:

    cforest, ctree, ctree_control, edge_simple, mob, mob_control, node_barplot,
    node_bivplot, node_boxplot, node_inner, node_surv, node_terminal, varimp

g
library(moreparty)
G3;Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
g
library(doParallel)
G3;Cargando paquete requerido: foreach
gG3;
Adjuntando el paquete: ‘foreach’

gG3;The following objects are masked from ‘package:purrr’:

    accumulate, when

gG3;Cargando paquete requerido: iterators
gG3;Cargando paquete requerido: parallel
g
library(strucchange)
library(ggparty)
G3;
Adjuntando el paquete: ‘ggparty’

gG3;The following object is masked from ‘package:ggeffects’:

    get_predictions

g
library(caret)
G3;Cargando paquete requerido: lattice
gG3;
Adjuntando el paquete: ‘caret’

gG3;The following object is masked from ‘package:purrr’:

    lift

g
library(moreparty)
library(randomForest)
G3;randomForest 4.7-1.2
gG3;Type rfNews() to see new features/changes/bug fixes.
gG3;
Adjuntando el paquete: ‘randomForest’

gG3;The following object is masked from ‘package:gridExtra’:

    combine

gG3;The following object is masked from ‘package:dplyr’:

    combine

gG3;The following object is masked from ‘package:ggplot2’:

    margin

g
library(pROC)
G3;Type 'citation("pROC")' for a citation.
gG3;
Adjuntando el paquete: ‘pROC’

gG3;The following objects are masked from ‘package:stats’:

    cov, smooth, var

g
library(corrplot)
G3;corrplot 0.95 loaded
g
library(rlang)
G3;
Adjuntando el paquete: ‘rlang’

gG3;The following objects are masked from ‘package:purrr’:

    %@%, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl, flatten_raw,
    invoke, splice

g
library(stringr)
library(beepr)
library(foreach)
library(permimp)
library(yardstick)
G3;
Adjuntando el paquete: ‘yardstick’

gG3;The following objects are masked from ‘package:caret’:

    precision, recall, sensitivity, specificity

gG3;The following object is masked from ‘package:readr’:

    spec

g

Define printall function

printall <- function(tibble) {
  print(tibble, width = Inf)
  }

Load geom_flat_violin plot

source("https://gist.githubusercontent.com/benmarwick/2a1bb0133ff568cbe28d/raw/fb53bd97121f7f9ce947837ef1a4c65a73bffb3f/geom_flat_violin.R")

Load previously created objects

# Define the folder path
folder_path <- here("objects", "RF")

# List all .RData or .rda files in the folder
rdata_files <- list.files(folder_path, full.names = TRUE)

# Load each file
lapply(rdata_files, load, envir = .GlobalEnv)
[[1]]
[1] "rf1"

[[2]]
[1] "predictions_rf1"

[[3]]
[1] "varimp_rf1"

[[4]]
[1] "rf10"

[[5]]
[1] "predictions_rf10"

[[6]]
[1] "varimp_rf10"

[[7]]
[1] "rf11"

[[8]]
[1] "predictions_rf11"

[[9]]
[1] "varimp_rf11"

[[10]]
[1] "rf12"

[[11]]
[1] "predictions_rf12"

[[12]]
[1] "varimp_rf12"

[[13]]
[1] "rf17"

[[14]]
[1] "predictions_rf17"

[[15]]
[1] "varimp_rf17"

[[16]]
[1] "rf18"

[[17]]
[1] "predictions_rf18"

[[18]]
[1] "varimp_rf18"

[[19]]
[1] "rf19"

[[20]]
[1] "predictions_rf19"

[[21]]
[1] "varimp_rf19"

[[22]]
[1] "rf2"

[[23]]
[1] "predictions_rf2"

[[24]]
[1] "varimp_rf2"

[[25]]
[1] "rf20"

[[26]]
[1] "predictions_rf20"

[[27]]
[1] "varimp_rf20"

[[28]]
[1] "rf21"

[[29]]
[1] "predictions_rf21"

[[30]]
[1] "rf23"

[[31]]
[1] "predictions_rf23"

[[32]]
[1] "rf3"

[[33]]
[1] "predictions_rf3"

[[34]]
[1] "varimp_rf3"

[[35]]
[1] "rf4"

[[36]]
[1] "predictions_rf4"

[[37]]
[1] "varimp_rf4"

[[38]]
[1] "rf9"

[[39]]
[1] "predictions_rf9"

[[40]]
[1] "varimp_rf9"

Read data

data_validation<-read_tsv(here(
  "data", "clean","final_RS_data_bands_S2_all_20250804.csv"))
Rows: 32730 Columns: 137
── Column specification ──────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr   (7): EUNISa_1, EUNISa_1_descr, EUNISa_2, EUNISa_2_descr, biogeo, unit, Lctnmth
dbl (130): PlotObservationID, EVI_auc_mar_oct, EVI_auc_slope, EVI_auc_threshold, EVI_avg_value...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

No parsing issues!

Some data managenemt

TO-DO: Missing data checks

Do when all RS data is ready!

Distributions all bioregions

Indices

# Define a function to create histograms
plot_histogram <- function(data, x_var, x_label) {
  ggplot(data %>%
           dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
         aes(x = !!sym(x_var))) +
    geom_histogram(color = "black", fill = "white") +
    labs(x = x_label, y = "Frequency") +
    theme_bw()
}
# Define a function to create plots with violin + boxplot + points
distr_plot <- function(data, y_vars, y_labels) {
  for (i in seq_along(y_vars)) {
    y_var <- y_vars[[i]]
    y_label <- y_labels[[i]]
    
    p <- ggplot(data = data %>%
                  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
                aes(x = EUNISa_1_descr, y = !!sym(y_var), fill = EUNISa_1_descr)) +
      geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
      geom_point(aes(y = !!sym(y_var), color = EUNISa_1_descr),
                 position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
      geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
      stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
      stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
                                                     label = length(x)),
                   geom = "text", aes(label = ..label..), vjust = 0.5) +
      labs(y = y_label, x = "EUNIS level 1") +
      scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
      guides(fill = FALSE, color = FALSE) +
      theme_bw() + coord_flip()
    
    print(p)
  }
}

Ranges of min and max:

range(data_validation$NDVI_max, na.rm = T) # NDVI_max > 1 (slightly)
[1] 0.06064505 1.08505480
range(data_validation$NDMI_max, na.rm = T) # NDMI_max > 1 (slightly)
[1] -0.2202634  1.0960134
range(data_validation$NDWI_max, na.rm = T)
[1] -0.7291356  0.5942999
range(data_validation$SAVI_max, na.rm = T)
[1] 0.02386599 0.85981347
range(data_validation$EVI_max, na.rm = T) # EVI_max > 1 (slightly)
[1] 0.04756044 1.19383584
range(data_validation$NDVI_min, na.rm = T)
[1] -0.7058081  0.8423390
range(data_validation$NDMI_min, na.rm = T)
[1] -0.5601429  0.5758041
range(data_validation$NDWI_min, na.rm = T) # NDWI_min < -1 (slightly)
[1] -1.014830371 -0.008258131
range(data_validation$SAVI_min, na.rm = T)
[1] -0.5367440  0.6335289
range(data_validation$EVI_min, na.rm = T) # EVI_min < -1!
[1] -1.5674420  0.7891647
nrow(data_validation %>% dplyr::filter(NDVI_max > 1))
[1] 18
nrow(data_validation %>% dplyr::filter(NDMI_max > 1))
[1] 51
nrow(data_validation %>% dplyr::filter(EVI_max > 1))
[1] 9
nrow(data_validation %>% dplyr::filter(NDWI_min < -1))
[1] 4
nrow(data_validation %>% dplyr::filter(EVI_min < -1))
[1] 10

Histograms to check that max and min values are ok:

plot_histogram(data_validation, "NDVI_max", "NDVI max")

plot_histogram(data_validation, "NDMI_max", "NDMI max")

plot_histogram(data_validation, "NDWI_max", "NDWI max")

plot_histogram(data_validation, "SAVI_max", "SAVI max")

plot_histogram(data_validation, "EVI_max", "EVI max")

plot_histogram(data_validation, "NDVI_min", "NDVI min")

plot_histogram(data_validation, "NDMI_min", "NDMI min")

plot_histogram(data_validation, "NDWI_min", "NDWI min")

plot_histogram(data_validation, "SAVI_min", "SAVI min")

plot_histogram(data_validation, "EVI_min", "EVI min")

nrow(data_validation %>%
       dplyr::filter(EVI_max > 1 | EVI_max < -1))
[1] 9
data_validation %>%
  dplyr::filter(EVI_max > 1 | EVI_max < -1) %>%
  count(biogeo, unit)

Most EVI values are ok!

Distribution plots:

distr_plot(data_validation %>% dplyr::filter(EVI_min > -0.5),
           c("NDVI_max", "EVI_max", "SAVI_max", "NDMI_max", "NDWI_max",
             "NDVI_min", "EVI_min", "SAVI_min", "NDMI_min", "NDWI_min"),
           c("NDVI_max", "EVI_max", "SAVI_max", "NDMI_max", "NDWI_max",
             "NDVI_min", "EVI_min", "SAVI_min", "NDMI_min", "NDWI_min"))

CH

distr_plot(data_validation, "canopy_height", "Canopy height (m)")

Show habitats with CH categories

ggplot(data_validation %>%
         mutate(CH_cat =
                  factor(
                    case_when(canopy_height == 0 ~ "0 m",
                              canopy_height > 0 & canopy_height <= 1 ~ "0-1 m",
                              canopy_height > 1 & canopy_height <=2 ~ "1-2 m",
                              canopy_height > 2 & canopy_height <=5 ~ "2-5 m",
                              canopy_height > 5 & canopy_height <=8 ~ "5-8 m",
                              canopy_height > 8 ~ "> 8 m",
                              is.na(canopy_height) ~ NA_character_),
                    levels = c(
                      "0 m", "0-1 m", "1-2 m", "2-5 m", "5-8 m", "> 8 m"))),
       aes(x = EUNISa_1_descr, fill = CH_cat)) +
  geom_bar() + theme_bw() + coord_flip() +
  scale_y_continuous(labels = label_number()) +
  scale_fill_viridis_d(direction = -1) +
  labs(x = "EUNIS level 1", fill = "Canopy height") +
  scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
  theme(legend.position = c(0.8, 0.75),
        legend.direction = "vertical")

Stats per habitat type

data_validation %>%
  group_by(EUNISa_1_descr) %>%
  summarise(across(canopy_height, list(
    mean = mean,
    median = median,
    sd = sd,
    min = min,
    max = max
    ), na.rm = TRUE))

Phenology

Only using NDVI- and SAVI-based values so far.

Maximum NDVI should be equal to value at peak?

nrow(data_validation %>% dplyr::filter(NDVI_pos_value  != NDVI_max))
[1] 584

Not sure why this happens, but in most cases the difference is small (< -0.1). Anyway, we should use only one of these two in the RF models.

plot_histogram(data_validation, "NDVI_sos_slope_doy", "NDVI_sos_slope_doy")

plot_histogram(data_validation, "NDVI_pos_doy", "NDVI_pos_doy")

plot_histogram(data_validation, "NDVI_eos_slope_doy", "NDVI_eos_slope_doy")

plot_histogram(data_validation, "EVI_sos_slope_doy", "EVI_sos_slope_doy")

plot_histogram(data_validation, "EVI_pos_doy", "EVI_pos_doy")

plot_histogram(data_validation, "EVI_eos_slope_doy", "EVI_eos_slope_doy")

plot_histogram(data_validation, "SAVI_sos_slope_doy", "SAVI_sos_slope_doy")

plot_histogram(data_validation, "SAVI_pos_doy", "SAVI_pos_doy")

plot_histogram(data_validation, "SAVI_eos_slope_doy", "SAVI_eos_slope_doy")

plot_histogram(data_validation, "NDVI_sos_threshold_doy", "NDVI_sos_threshold_doy")

plot_histogram(data_validation, "NDVI_pos_doy", "NDVI_pos_doy")

plot_histogram(data_validation, "NDVI_eos_threshold_doy", "NDVI_eos_threshold_doy")

plot_histogram(data_validation, "EVI_sos_threshold_doy", "EVI_sos_threshold_doy")

plot_histogram(data_validation, "EVI_pos_doy", "EVI_pos_doy")

plot_histogram(data_validation, "EVI_eos_threshold_doy", "EVI_eos_threshold_doy")

plot_histogram(data_validation, "SAVI_sos_threshold_doy", "SAVI_sos_threshold_doy")

plot_histogram(data_validation, "SAVI_pos_doy", "SAVI_pos_doy")

plot_histogram(data_validation, "SAVI_eos_threshold_doy", "SAVI_eos_threshold_doy")

plot_histogram(data_validation, "NDVI_sos_slope_value", "NDVI_sos_slope_value")

plot_histogram(data_validation, "NDVI_pos_value", "NDVI_pos_value")

plot_histogram(data_validation, "NDVI_eos_slope_value", "NDVI_eos_slope_value")

plot_histogram(data_validation, "EVI_sos_slope_value", "EVI_sos_slope_value")

plot_histogram(data_validation, "EVI_pos_value", "EVI_pos_value")

plot_histogram(data_validation, "EVI_eos_slope_value", "EVI_eos_slope_value")

plot_histogram(data_validation, "NDVI_sos_threshold_value", "NDVI_sos_threshold_value")

plot_histogram(data_validation, "NDVI_pos_value", "NDVI_pos_value")

plot_histogram(data_validation, "NDVI_eos_threshold_value", "NDVI_eos_threshold_value")

plot_histogram(data_validation, "EVI_sos_threshold_value", "EVI_sos_threshold_value")

plot_histogram(data_validation, "EVI_pos_value", "EVI_pos_value")

plot_histogram(data_validation, "EVI_eos_threshold_value", "EVI_eos_threshold_value")

plot_histogram(data_validation, "NDVI_slope_gsd", "NDVI_slope_gsd")

plot_histogram(data_validation, "EVI_slope_gsd", "EVI_slope_gsd")

plot_histogram(data_validation, "SAVI_slope_gsd", "SAVI_slope_gsd")

plot_histogram(data_validation, "NDVI_diff_pos_sos_slope_value",
               "NDVI_diff_pos_sos_slope_value")

plot_histogram(data_validation, "EVI_diff_pos_sos_slope_value",
               "EVI_diff_pos_sos_slope_value")

plot_histogram(data_validation, "SAVI_diff_pos_sos_slope_value",
               "SAVI_diff_pos_sos_slope_value")

plot_histogram(data_validation, "NDVI_diff_pos_eos_slope_value",
               "NDVI_diff_pos_eos_slope_value")

plot_histogram(data_validation, "EVI_diff_pos_eos_slope_value",
               "EVI_diff_pos_eos_slope_value")

plot_histogram(data_validation, "SAVI_diff_pos_eos_slope_value",
               "SAVI_diff_pos_eos_slope_value")

plot_histogram(data_validation, "NDVI_diff_pos_sos_slope_doy",
               "NDVI_diff_pos_sos_slope_doy")

plot_histogram(data_validation, "EVI_diff_pos_sos_slope_doy",
               "EVI_diff_pos_sos_slope_doy")

plot_histogram(data_validation, "SAVI_diff_pos_sos_slope_doy",
               "SAVI_diff_pos_sos_slope_doy")

plot_histogram(data_validation, "NDVI_diff_pos_eos_slope_doy",
               "NDVI_diff_pos_eos_slope_doy")

plot_histogram(data_validation, "EVI_diff_pos_eos_slope_doy", 
               "EVI_diff_pos_eos_slope_doy")

plot_histogram(data_validation, "SAVI_diff_pos_eos_slope_doy", 
               "SAVI_diff_pos_eos_slope_doy")

plot_histogram(data_validation, "NDVI_threshold_gsd", "NDVI_threshold_gsd")

plot_histogram(data_validation, "EVI_threshold_gsd", "EVI_threshold_gsd")

plot_histogram(data_validation, "SAVI_threshold_gsd", "SAVI_threshold_gsd")

plot_histogram(data_validation, "NDVI_diff_pos_sos_threshold_value",
               "NDVI_diff_pos_sos_threshold_value")

plot_histogram(data_validation, "EVI_diff_pos_sos_threshold_value",
               "EVI_diff_pos_sos_threshold_value")

plot_histogram(data_validation, "SAVI_diff_pos_sos_threshold_value",
               "SAVI_diff_pos_sos_threshold_value")

plot_histogram(data_validation, "NDVI_diff_pos_eos_threshold_value",
               "NDVI_diff_pos_eos_threshold_value")

plot_histogram(data_validation, "EVI_diff_pos_eos_threshold_value",
               "EVI_diff_pos_eos_threshold_value")

plot_histogram(data_validation, "SAVI_diff_pos_eos_threshold_value",
               "SAVI_diff_pos_eos_threshold_value")

plot_histogram(data_validation, "NDVI_diff_pos_sos_threshold_doy",
               "NDVI_diff_pos_sos_threshold_doy")

plot_histogram(data_validation, "EVI_diff_pos_sos_threshold_doy",
               "EVI_diff_pos_sos_threshold_doy")

plot_histogram(data_validation, "SAVI_diff_pos_sos_threshold_doy",
               "SAVI_diff_pos_sos_threshold_doy")

plot_histogram(data_validation, "NDVI_diff_pos_eos_threshold_doy",
               "NDVI_diff_pos_eos_threshold_doy")

plot_histogram(data_validation, "EVI_diff_pos_eos_threshold_doy", 
               "EVI_diff_pos_eos_threshold_doy")

plot_histogram(data_validation, "SAVI_diff_pos_eos_threshold_doy", 
               "SAVI_diff_pos_eos_threshold_doy")

plot_histogram(data_validation, "NDVI_auc_slope", "NDVI_auc_slope")

plot_histogram(data_validation, "EVI_auc_slope", "EVI_auc_slope")

plot_histogram(data_validation, "SAVI_auc_slope", "SAVI_auc_slope")

plot_histogram(data_validation, "NDVI_auc_threshold", "NDVI_auc_threshold")

plot_histogram(data_validation, "EVI_auc_threshold", "EVI_auc_threshold")

plot_histogram(data_validation, "SAVI_auc_threshold", "SAVI_auc_threshold")

distr_plot(data_validation,
           c("NDVI_sos_slope_value","NDVI_pos_value", "NDVI_eos_slope_value",
             "EVI_sos_slope_value","EVI_pos_value", "EVI_eos_slope_value",
             "SAVI_sos_slope_value", "SAVI_pos_value", "SAVI_eos_slope_value",
             "NDVI_sos_slope_doy","NDVI_pos_doy", "NDVI_eos_slope_doy",
             "EVI_sos_slope_doy","EVI_pos_doy", "EVI_eos_slope_doy",
             "SAVI_sos_slope_doy", "SAVI_pos_doy", "SAVI_eos_slope_doy",
             "NDVI_sos_threshold_value","NDVI_pos_value", "NDVI_eos_threshold_value",
             "EVI_sos_threshold_value","EVI_pos_value", "EVI_eos_threshold_value",
             "SAVI_sos_threshold_value", "SAVI_pos_value", "SAVI_eos_threshold_value",
             "NDVI_sos_threshold_doy","NDVI_pos_doy", "NDVI_eos_threshold_doy",
             "EVI_sos_threshold_doy","EVI_pos_doy", "EVI_eos_threshold_doy",
             "SAVI_sos_threshold_doy", "SAVI_pos_doy", "SAVI_eos_threshold_doy"),
           c("NDVI_sos_slope_value","NDVI_pos_value", "NDVI_eos_slope_value",
             "EVI_sos_slope_value","EVI_pos_value", "EVI_eos_slope_value",
             "SAVI_sos_slope_value", "SAVI_pos_value", "SAVI_eos_slope_value",
             "NDVI_sos_slope_doy","NDVI_pos_doy", "NDVI_eos_slope_doy",
             "EVI_sos_slope_doy","EVI_pos_doy", "EVI_eos_slope_doy",
             "SAVI_sos_slope_doy", "SAVI_pos_doy", "SAVI_eos_slope_doy",
             "NDVI_sos_threshold_value","NDVI_pos_value", "NDVI_eos_threshold_value",
             "EVI_sos_threshold_value","EVI_pos_value", "EVI_eos_threshold_value",
             "SAVI_sos_threshold_value", "SAVI_pos_value", "SAVI_eos_threshold_value",
             "NDVI_sos_threshold_doy","NDVI_pos_doy", "NDVI_eos_threshold_doy",
             "EVI_sos_threshold_doy","EVI_pos_doy", "EVI_eos_threshold_doy",
             "SAVI_sos_threshold_doy", "SAVI_pos_doy", "SAVI_eos_threshold_doy")
           )

distr_plot(data_validation,
           c("NDVI_slope_gsd","EVI_slope_gsd", "SAVI_slope_gsd",
             "NDVI_diff_pos_sos_slope_value", "EVI_diff_pos_sos_slope_value",
             "SAVI_diff_pos_sos_slope_value",
             "NDVI_diff_pos_eos_slope_value", "EVI_diff_pos_eos_slope_value",
             "SAVI_diff_pos_eos_slope_value",
             "NDVI_diff_pos_sos_slope_doy", "EVI_diff_pos_sos_slope_doy",
             "SAVI_diff_pos_sos_slope_doy",
             "NDVI_diff_pos_eos_slope_doy", "EVI_diff_pos_eos_slope_doy",
             "SAVI_diff_pos_eos_slope_doy",
             "NDVI_threshold_gsd","EVI_threshold_gsd", "SAVI_threshold_gsd",
             "NDVI_diff_pos_sos_threshold_value", "EVI_diff_pos_sos_threshold_value",
             "SAVI_diff_pos_sos_threshold_value",
             "NDVI_diff_pos_eos_threshold_value", "EVI_diff_pos_eos_threshold_value",
             "SAVI_diff_pos_eos_threshold_value",
             "NDVI_diff_pos_sos_threshold_doy", "EVI_diff_pos_sos_threshold_doy",
             "SAVI_diff_pos_sos_threshold_doy",
             "NDVI_diff_pos_eos_threshold_doy", "EVI_diff_pos_eos_threshold_doy",
             "SAVI_diff_pos_eos_threshold_doy"),
           c("NDVI_slope_gsd","EVI_slope_gsd", "SAVI_slope_gsd",
             "NDVI_diff_pos_sos_slope_value", "EVI_diff_pos_sos_slope_value",
             "SAVI_diff_pos_sos_slope_value",
             "NDVI_diff_pos_eos_slope_value", "EVI_diff_pos_eos_slope_value",
             "SAVI_diff_pos_eos_slope_value",
             "NDVI_diff_pos_sos_slope_doy", "EVI_diff_pos_sos_slope_doy",
             "SAVI_diff_pos_sos_slope_doy",
             "NDVI_diff_pos_eos_slope_doy", "EVI_diff_pos_eos_slope_doy",
             "SAVI_diff_pos_eos_slope_doy",
             "NDVI_threshold_gsd","EVI_threshold_gsd", "SAVI_threshold_gsd",
             "NDVI_diff_pos_sos_threshold_value", "EVI_diff_pos_sos_threshold_value",
             "SAVI_diff_pos_sos_threshold_value",
             "NDVI_diff_pos_eos_threshold_value", "EVI_diff_pos_eos_threshold_value",
             "SAVI_diff_pos_eos_threshold_value",
             "NDVI_diff_pos_sos_threshold_doy", "EVI_diff_pos_sos_threshold_doy",
             "SAVI_diff_pos_sos_threshold_doy",
             "NDVI_diff_pos_eos_threshold_doy", "EVI_diff_pos_eos_threshold_doy",
             "SAVI_diff_pos_eos_threshold_doy")
           )

distr_plot(data_validation,
           c("NDVI_auc_slope", "EVI_auc_slope", "SAVI_auc_slope",
             "NDVI_auc_threshold", "EVI_auc_threshold", "SAVI_auc_threshold",
             "NDVI_auc_mar_oct", "EVI_auc_mar_oct", "SAVI_auc_mar_oct"),
             c("NDVI_auc_slope", "EVI_auc_slope", "SAVI_auc_slope",
               "NDVI_auc_threshold", "EVI_auc_threshold", "SAVI_auc_threshold",
               "NDVI_auc_mar_oct", "EVI_auc_mar_oct", "SAVI_auc_mar_oct"))

TBD: Distributions per bioregion

# Define a function to create plots with violin + boxplot + points
distr_plot_biogeo <- function(data, y_vars, y_labels) {
  plots <- list()
  
  for (i in seq_along(y_vars)) {
    y_var <- y_vars[[i]]
    y_label <- y_labels[[i]]
    
    p <- ggplot(data = data %>%
                  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
                aes(x = EUNISa_1_descr, y = !!sym(y_var), fill = EUNISa_1_descr)) +
      geom_flat_violin(position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
      geom_point(aes(y = !!sym(y_var), color = EUNISa_1_descr),
                 position = position_jitter(width = 0.15), size = 1, alpha = 0.25) +
      geom_boxplot(width = 0.2, outlier.shape = NA, alpha = 0.5) +
      stat_summary(fun.y = mean, geom = "point", shape = 20, size = 1) +
      stat_summary(fun.data = function(x) data.frame(y = max(x) + 0.1,
                                                     label = length(x)),
                   geom = "text", aes(label = ..label..), vjust = 0.5) +
      labs(y = y_label, x = "EUNISa_1_descr") +
      scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
      guides(fill = FALSE, color = FALSE) +
      theme_bw() + coord_flip() + facet_wrap(~ biogeo)
    
    plots[[y_var]] <- p
  }
  
  return(plots)
}

Indices

Distribution plots:

CH

distr_plot_biogeo(data_validation, "canopy_height", "Canopy height (m)")
$canopy_height

Phenology

Define functions for RF models

Function for fitting RF models

RF models fitted using the conditional inference version of random forest (first cforest in package party, now fastcforest in package moreparty). Suggested if the data are highly correlated. Cforest is more stable in deriving variable importance values in the presence of highly correlated variables, thus providing better accuracy in calculating variable importance (ref below).

Hothorn, T., Hornik, K. and Zeileis, A. (2006) Unbiased Recursive Portioning: A Conditional Inference Framework. Journal of Computational and Graphical Statistics, 15, 651- 674. http://dx.doi.org/10.1198/106186006X133933

Choose the hyperparameter mtry based on the square root of the number of predictor variables:

Hastie, T., Tibshirani, R., & Friedman, J. (2009). The elements of statistical learning: Data mining, inference, and prediction. Springer Science & Business Media.

Maybe TO_DO: We variated ntree from 50 to 800 in steps of 50, leaving mtry constant at 2. Tis parameter variation showed that ntree=500 was optimal, while higher ntree led to no further model improvement (Supplementary Fig. S10). Subsequently, the hyperparameter mtry was varied from 2 to 8 with constant ntree=500. Here, mtry=3 led to the best results in almost all cases (Supplementary Fig. S11). Consequently, we chose ntree=500 and mtry=3 for our main analysis across all study sites.

Define a function to run fastcforest models:

run_rf <- function(vars_RF, train_data, response_var, ntree = 500) 
  {
  
  # Detect and register available cores (leave one free)
  n_cores <- parallel::detectCores() - 1
  cl <- makeCluster(n_cores)
  registerDoParallel(cl)
  
  train_name <- deparse(substitute(train_data))
  
  # Export necessary variables to the cluster
  clusterExport(cl, varlist = c("vars_RF", "train_data", "response_var"),
                envir = environment())

  
  # Set seed for reproducibility
  set.seed(123)
  
  # Measure execution time
  execution_time <- system.time({
    rf_model <- fastcforest(
      formula = reformulate(vars_RF, response = response_var),
      data = train_data,
      controls = party::cforest_control(
        mtry = round(sqrt(length(vars_RF))),
        ntree = ntree
      ),
      parallel = TRUE
    )
  })
  
  # Stop the cluster
  stopCluster(cl)
  
  # Return both the model and execution time
  list(model = rf_model, time = execution_time)
}

Function to compute variable importance

# compute_varimp <- function(model, nperm = 100, 
#                                    n_cores = parallel::detectCores() - 1) {
#   # Set up parallel backend
#   cl <- makeCluster(n_cores)
#   registerDoParallel(cl)
#   
#   # Measure execution time
#   execution_time <- system.time({
#     varimp_list <- foreach(i = 1:nperm, .combine = '+', 
#                            .packages = "party") %dopar% {
#       varimp(model, conditional = FALSE, nperm = 1)
#     }
#   })
#   
#   stopCluster(cl)
#   
#   # Average the results
#   varimp_avg <- varimp_list / nperm
#   
#   return(list(varimp = varimp_avg, time = execution_time))
# }

Using permimp() en permimp package: https://cran.r-project.org/web/packages/permimp/vignettes/permimp-package.html#fn1

compute_varimp <- function(model, nperm = 100) {

  # Measure execution time
  execution_time <- system.time({
    varimp_result <- permimp(model, conditional = FALSE, progressBar = TRUE)
  })

  return(list(varimp = varimp_result, time = execution_time))
}

Function to compute CONDITIONAL variable importance

compute_varimp_cond <- function(model, nperm = 100) {

  # Measure execution time
  execution_time <- system.time({
    varimp_result <- permimp(model, conditional = TRUE, progressBar = TRUE)
  })

  return(list(varimp = varimp_result, time = execution_time))
}

Function to compute ROC (level 1)

compute_roc_level1 <- function(model, test_data) {
  # Measure execution time
  execution_time <- system.time({
    # Step 1: Predict probabilities
    probabilities <- predict(model, newdata = test_data, type = "prob")
    
    # Step 2: Convert list of matrices to a proper data frame
    prob_matrix <- t(sapply(probabilities, as.vector))
    prob_df <- as.data.frame(prob_matrix)
    colnames(prob_df) <- c("Q", "R", "S", "T")
    
    # Step 3: Prepare actual class labels
    actual <- factor(test_data$EUNISa_1, levels = c("Q", "R", "S", "T"))
    
    # Step 4: Binarize actual labels
    actual_bin <- model.matrix(~ actual - 1)
    colnames(actual_bin) <- gsub("actual", "", colnames(actual_bin))
    
    # Step 5: Compute ROC data for each class
    roc_data <- lapply(levels(actual), function(class) {
      roc_obj <- roc(actual_bin[, class], prob_df[[class]])
      auc_val <- round(auc(roc_obj), 3)
      data.frame(
        FPR = rev(roc_obj$specificities),
        TPR = rev(roc_obj$sensitivities),
        Class = paste0(class, " (AUC = ", auc_val, ")")
      )
    }) %>% bind_rows()
  })
  
  # Return both ROC data and execution time
  return(list(roc = roc_data, time = execution_time))
}

Function to compute ROC (level 2)

compute_roc_level2 <- function(model, test_data) {
  # Measure execution time
  execution_time <- system.time({
    # Step 1: Predict probabilities
    probabilities <- predict(model, newdata = test_data, type = "prob")
    
    # Step 2: Convert list of matrices to a proper data frame
    prob_matrix <- t(sapply(probabilities, as.vector))
    prob_df <- as.data.frame(prob_matrix)
    colnames(prob_df) <- c("Q1", "Q2", "Q4", "Q5", "R1", "R2", "R3", "R4", "R5",
                           "R6", "S3", "S4", "T1", "T3")
    
    # Step 3: Prepare actual class labels
    actual <- factor(test_data$EUNISa_2, 
                     levels = c("Q1", "Q2", "Q4", "Q5", "R1", "R2", "R3", "R4",
                                "R5", "R6", "S3", "S4", "T1", "T3"))
    
    # Step 4: Binarize actual labels
    actual_bin <- model.matrix(~ actual - 1)
    colnames(actual_bin) <- gsub("actual", "", colnames(actual_bin))
    
    # Step 5: Compute ROC data for each class
    roc_data <- lapply(levels(actual), function(class) {
      roc_obj <- roc(actual_bin[, class], prob_df[[class]])
      auc_val <- round(auc(roc_obj), 3)
      data.frame(
        FPR = rev(roc_obj$specificities),
        TPR = rev(roc_obj$sensitivities),
        Class = paste0(class, " (AUC = ", auc_val, ")")
      )
    }) %>% bind_rows()
  })
  
  # Return both ROC data and execution time
  return(list(roc = roc_data, time = execution_time))
}

Define lists of predictor vars

Moments with slope method

vars_RF_slope <- c(
  # Min values of all indices
  "NDVI_min", "EVI_min", "NDMI_min", "NDWI_min", "SAVI_min",
  # Max values of NDMI and NDWI
  "NDMI_max", "NDWI_max",
  # AUC of NDVI, EVI and SAVI
  "NDVI_auc_slope", "EVI_auc_slope", "SAVI_auc_slope",
  # Values of NDVI, EVI and SAVI at sos, pos and eos
  "NDVI_sos_slope_value", "NDVI_pos_value", "NDVI_eos_slope_value",
  "EVI_sos_slope_value", "EVI_pos_value", "EVI_eos_slope_value", 
  "SAVI_sos_slope_value", "SAVI_pos_value", "SAVI_eos_slope_value",
  # Differences pos-sos in value and doy
  "NDVI_diff_pos_sos_slope_value", "EVI_diff_pos_sos_slope_value",
  "SAVI_diff_pos_sos_slope_value","NDVI_diff_pos_sos_slope_doy", 
  "EVI_diff_pos_sos_slope_doy", "SAVI_diff_pos_sos_slope_doy",
  # Differences pos-eos in value and doy
  "NDVI_diff_pos_eos_slope_value", "EVI_diff_pos_eos_slope_value",
  "SAVI_diff_pos_eos_slope_value", "NDVI_diff_pos_eos_slope_doy", 
  "EVI_diff_pos_eos_slope_doy", "SAVI_diff_pos_eos_slope_doy",
  # Growing season duration
  "NDVI_slope_gsd", "EVI_slope_gsd", "SAVI_slope_gsd",
  # Canopy height
  "canopy_height")

Moments with threshold method

vars_RF_threshold <- c(
  # Min values of all indices
  "NDVI_min", "EVI_min", "NDMI_min", "NDWI_min", "SAVI_min",
  # Max values of NDMI and NDWI
  "NDMI_max", "NDWI_max",
  # AUC of NDVI, EVI and SAVI
  "NDVI_auc_threshold", "EVI_auc_threshold", "SAVI_auc_threshold",
  # Values of NDVI, EVI and SAVI at sos, pos and eos
  "NDVI_sos_threshold_value", "NDVI_pos_value", "NDVI_eos_threshold_value",
  "EVI_sos_threshold_value", "EVI_pos_value", "EVI_eos_threshold_value", 
  "SAVI_sos_threshold_value", "SAVI_pos_value", "SAVI_eos_threshold_value",
  # Differences pos-sos in value and doy
  "NDVI_diff_pos_sos_threshold_value", "EVI_diff_pos_sos_threshold_value",
  "SAVI_diff_pos_sos_threshold_value","NDVI_diff_pos_sos_threshold_doy", 
  "EVI_diff_pos_sos_threshold_doy", "SAVI_diff_pos_sos_threshold_doy",
  # Differences pos-eos in value and doy
  "NDVI_diff_pos_eos_threshold_value", "EVI_diff_pos_eos_threshold_value",
  "SAVI_diff_pos_eos_threshold_value", "NDVI_diff_pos_eos_threshold_doy", 
  "EVI_diff_pos_eos_threshold_doy", "SAVI_diff_pos_eos_threshold_doy",
  # Growing season duration
  "NDVI_threshold_gsd", "EVI_threshold_gsd", "SAVI_threshold_gsd",
  # Canopy height
  "canopy_height")

Months method

vars_RF_months <- c(
  # Min values of all indices
  "NDVI_min", "EVI_min", "NDMI_min", "NDWI_min", "SAVI_min",
  # Max values of NDMI and NDWI
  "NDMI_max", "NDWI_max",
  # AUC of NDVI, EVI and SAVI between march and oct
  "NDVI_auc_mar_oct", "EVI_auc_mar_oct", "SAVI_auc_mar_oct",
  # Values of NDVI, EVI and SAVI at march, pos and oct
  "NDVI_avg_value_03", "NDVI_pos_value", "NDVI_avg_value_10",
  "EVI_avg_value_03", "EVI_pos_value", "EVI_avg_value_10", 
  "SAVI_avg_value_03", "SAVI_pos_value", "SAVI_avg_value_10",
  # Differences pos-march in value and doy
  "NDVI_diff_pos_march_value", "EVI_diff_pos_march_value",
  "SAVI_diff_pos_march_value","NDVI_diff_pos_march_doy", 
  "EVI_diff_pos_march_doy", "SAVI_diff_pos_march_doy",
  # Differences pos-oct in value and doy
  "NDVI_diff_pos_oct_value", "EVI_diff_pos_oct_value",
  "SAVI_diff_pos_oct_value", "NDVI_diff_pos_oct_doy", 
  "EVI_diff_pos_oct_doy", "SAVI_diff_pos_oct_doy",
  # Canopy height
  "canopy_height")

Rough validation

Define a set of rules for a first validation of ALL ReSurvey data (“Expert-based” rules). Not very ambitious, only taking out observations that are clearly wrong on the basis of indicator values.

Create column for first validation based on different indicators, where “wrong” is noted when the validation rule is not met.

Define rules:

data_validation <-
  data_validation %>%
  mutate(
    valid_1_NDWI = case_when(
      # Points that are basically water
      NDWI_max > 0.3 ~ "wrong",
      TRUE ~ NA_character_),
    valid_1_CH = case_when(
      # R & Q points with high CH
      EUNISa_1 %in% c("R", "Q") & canopy_height > 2 ~ "wrong",
      # T points with low CH
      EUNISa_1 == "T" & canopy_height < 3 ~ "wrong",
      # S points with high CH
      EUNISa_1 == "S" & canopy_height > 3 ~ "wrong",
      TRUE ~ NA_character_),
    # No rules for NDVI, we will take out observations based on distributions
    # Count how many validation rules are not met
    valid_1_count = rowSums(across(c(valid_1_NDWI, valid_1_CH), ~ . == "wrong"),
                            na.rm = TRUE),
    # Points where at least 1 rule not met
    valid_1 = if_else(valid_1_count > 0, "At least 1 rule broken",
                      "No rules broken so far")
    )

Data to use in RF models

filtered_data0_slope <- data_validation %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  # Remove all rows with wrong values of indices (not between -1 and 1)
  dplyr::filter(EVI_max <= 1 & EVI_min >= -1) %>%
  dplyr::filter(NDVI_max <= 1) %>%
  dplyr::filter(NDMI_max <= 1) %>%
  dplyr::filter(NDWI_min >= -1) %>%
  # Remove rows with missing values in predictors
  dplyr::filter(if_all(all_of(vars_RF_slope), ~ !is.na(.))) %>%
  # Select only variables needed
  select(PlotObservationID, EUNISa_1, all_of(vars_RF_slope), Lctnmth, valid_1) %>%
  # Keep only rows with differences > 0
  dplyr::filter(if_all(contains("diff"), ~ .x > 0))
filtered_data0_threshold <- data_validation %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  # Remove all rows with wrong values of indices (not between -1 and 1)
  dplyr::filter(EVI_max <= 1 & EVI_min >= -1) %>%
  dplyr::filter(NDVI_max <= 1) %>%
  dplyr::filter(NDMI_max <= 1) %>%
  dplyr::filter(NDWI_min >= -1) %>%
  # Remove rows with missing values in predictors
  dplyr::filter(if_all(all_of(vars_RF_threshold), ~ !is.na(.))) %>%
  # Select only variables needed
  select(PlotObservationID, EUNISa_1, all_of(vars_RF_threshold), Lctnmth, valid_1) %>%
  # Keep only rows with differences > 0
  dplyr::filter(if_all(contains("diff"), ~ .x > 0))
filtered_data0_months <- data_validation %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  # Remove all rows with wrong values of indices (not between -1 and 1)
  dplyr::filter(EVI_max <= 1 & EVI_min >= -1) %>%
  dplyr::filter(NDVI_max <= 1) %>%
  dplyr::filter(NDMI_max <= 1) %>%
  dplyr::filter(NDWI_min >= -1) %>%
  # Remove rows with missing values in predictors
  dplyr::filter(if_all(all_of(vars_RF_months), ~ !is.na(.))) %>%
  # Select only variables needed
  select(PlotObservationID, EUNISa_1, all_of(vars_RF_months), Lctnmth, valid_1) %>%
  # Keep only rows with differences > 0
  dplyr::filter(if_all(contains("diff"), ~ .x > 0)) 

TBD: Correlation

Correlation of all variables to be included in RF models:

corrplot(filtered_data0_slope %>% 
           dplyr::select(all_of(vars_RF_slope)) %>%
           cor(use = "pairwise.complete.obs"),
         method = "color", type = "upper", tl.col = "black", tl.srt = 45)

# Compute correlation matrix
cor_matrix_slope <- filtered_data0_slope %>%
  dplyr::select(all_of(vars_RF_slope)) %>%
  cor(use = "pairwise.complete.obs")

# Set the diagonal to 0 to ignore self-correlation
diag(cor_matrix_slope) <- 0

# Find variables where all absolute correlations are < 0.7
keep_vars <- apply(abs(cor_matrix_slope), 1, function(x) all(x < 0.7))

# Subset the original data frame to keep only those variables
filtered_data0_slope_nocorr <- (filtered_data0_slope %>%
  dplyr::select(all_of(vars_RF_slope)))[, keep_vars]

Fit RF models (level 1)

Without refinement

Get filtered data

# 1: Slope method, no previous validation, GPS points
filtered_data1_slope <- filtered_data0_slope %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 2: Slope method, no previous validation, GPS diff points
filtered_data2_slope <- filtered_data0_slope %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 3: Slope method, rough validation, GPS points
filtered_data3_slope <- filtered_data0_slope %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 4: Slope method, rough validation, GPS diff points
filtered_data4_slope <- filtered_data0_slope %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 5: Slope method, rough validation + refinement 10-90th, GPS points
# 6: Slope method, rough validation + refinement 10-90th, GPS diff points
# 7: Slope method, rough validation + refinement 20-80th, GPS points
# 8: Slope method, rough validation + refinement 20-80th, GPS diff points

# 9: Threshold method, no previous validation, GPS points
filtered_data9_threshold <- filtered_data0_threshold %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 10: Threshold method, no previous validation, GPS diff points
filtered_data10_threshold <- filtered_data0_threshold %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 11: Threshold method, rough validation, GPS points
filtered_data11_threshold <- filtered_data0_threshold %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 12: Threshold method, rough validation, GPS diff points
filtered_data12_threshold <- filtered_data0_threshold %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 13: Threshold method, rough validation + refinement 10-90th, GPS points
# 14: Threshold method, rough validation + refinement 10-90th, GPS diff points
# 15: Threshold method, rough validation + refinement 20-80th, GPS points
# 16: Threshold method, rough validation + refinement 20-80th, GPS diff points

# 17: Months method, no previous validation, GPS points
filtered_data17_months <- filtered_data0_months %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 18: Months method, no previous validation, GPS diff points
filtered_data18_months <- filtered_data0_months %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  select(-Lctnmth, -valid_1)

# 19: Months method, rough validation, GPS points
filtered_data19_months <- filtered_data0_months %>%
  # Select only GPS points
  dplyr::filter(Lctnmth == "Location with GPS" |
                  Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 20: Months method, rough validation, GPS diff points
filtered_data20_months <- filtered_data0_months %>%
  # Select only GPS diff points
  dplyr::filter(Lctnmth == "Location with differential GPS") %>%
  # Filter out points that have not passed the rough validation
  dplyr::filter(valid_1 == "No rules broken so far") %>%
  select(-Lctnmth, -valid_1)

# 22: Months method, rough validation + refinement 10-90th, GPS diff points
# 23: Months method, rough validation + refinement 20-80th, GPS points
# 24: Months method, rough validation + refinement 20-80th, GPS diff points

N points per category

bind_rows(
  filtered_data1_slope %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data1_slope") %>%
    select(data, Q, R, S, T),
  filtered_data2_slope %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data2_slope") %>%
    select(data, Q, R, S, T),
  filtered_data3_slope %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data3_slope") %>%
    select(data, Q, R, S, T),
  filtered_data4_slope %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data4_slope") %>%
    select(data, Q, R, S, T),
  # filtered_data5_slope %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data5_slope") %>%
  #   select(data, Q, R, S, T),
  # iltered_data6_slope %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data6_slope") %>%
  #   select(data, Q, R, S, T),
  # filtered_data7_slope %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data7_slope") %>%
  #   select(data, Q, R, S, T),
  # filtered_data8_slope %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data8_slope") %>%
  #   select(data, Q, R, S, T),
  filtered_data9_threshold %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data9_threshold") %>%
    select(data, Q, R, S, T),
  filtered_data10_threshold %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data9_threshold") %>%
    select(data, Q, R, S, T),
  filtered_data11_threshold %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data9_threshold") %>%
    select(data, Q, R, S, T),
  filtered_data12_threshold %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data9_threshold") %>%
    select(data, Q, R, S, T),
  # filtered_data13_threshold %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data9_threshold") %>%
  #   select(data, Q, R, S, T),
  # filtered_data14_threshold %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data9_threshold") %>%
  #   select(data, Q, R, S, T),
  # filtered_data15_threshold %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data9_threshold") %>%
  #   select(data, Q, R, S, T),
  # filtered_data16_threshold %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data9_threshold") %>%
  #   select(data, Q, R, S, T),
  filtered_data17_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data17_months") %>%
    select(data, Q, R, S, T),
  filtered_data18_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data18_months") %>%
    select(data, Q, R, S, T),
  filtered_data19_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data19_months") %>%
    select(data, Q, R, S, T),
  filtered_data20_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>%
    mutate(data = "filtered_data20_months") %>%
    select(data, Q, R, S, T)
  # filtered_data21_months %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data21_months") %>%
  #   select(data, Q, R, S, T),
  # filtered_data22_months %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data22_months") %>%
  #   select(data, Q, R, S, T),
  # filtered_data23_months %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data23_months") %>%
  #   select(data, Q, R, S, T),
  # filtered_data24_months %>% count(EUNISa_1) %>%
  #   pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
  #   mutate(data = "filtered_data24_months") %>%
  #   select(data, Q, R, S, T),
  )

Split into training and test data sets

set.seed(123)
train_indices1 <- sample(1:nrow(filtered_data1_slope), 
                         0.7 * nrow(filtered_data1_slope))
train_indices2 <- sample(1:nrow(filtered_data2_slope), 
                         0.7 * nrow(filtered_data2_slope))
train_indices3 <- sample(1:nrow(filtered_data3_slope), 
                         0.7 * nrow(filtered_data3_slope))
train_indices4 <- sample(1:nrow(filtered_data4_slope), 
                         0.7 * nrow(filtered_data4_slope))
# train_indices5 <- sample(1:nrow(filtered_data5_slope),
#                          0.7 * nrow(filtered_data5_slope))
# train_indices6 <- sample(1:nrow(filtered_data6_slope), 
#                          0.7 * nrow(filtered_data6_slope))
# train_indices7 <- sample(1:nrow(filtered_data7_slope), 
#                          0.7 * nrow(filtered_data7_slope))
# train_indices8 <- sample(1:nrow(filtered_data8_slope),
#                          0.7 * nrow(filtered_data8_slope))
train_indices9 <- sample(1:nrow(filtered_data9_threshold), 
                         0.7 * nrow(filtered_data9_threshold))
train_indices10 <- sample(1:nrow(filtered_data10_threshold), 
                          0.7 * nrow(filtered_data10_threshold))
train_indices11 <- sample(1:nrow(filtered_data11_threshold), 
                          0.7 * nrow(filtered_data11_threshold))
train_indices12 <- sample(1:nrow(filtered_data12_threshold), 
                          0.7 * nrow(filtered_data12_threshold))
# train_indices13 <- sample(1:nrow(filtered_data13_threshold), 
#                           0.7 * nrow(filtered_data13_threshold))
# train_indices14 <- sample(1:nrow(filtered_data14_threshold), 
#                           0.7 * nrow(filtered_data14_threshold))
# train_indices15 <- sample(1:nrow(filtered_data15_threshold), 
#                           0.7 * nrow(filtered_data15_threshold))
# train_indices16 <- sample(1:nrow(filtered_data16_threshold), 
#                           0.7 * nrow(filtered_data16_threshold))
train_indices17 <- sample(1:nrow(filtered_data17_months), 
                          0.7 * nrow(filtered_data17_months))
train_indices18 <- sample(1:nrow(filtered_data18_months), 
                          0.7 * nrow(filtered_data18_months))
train_indices19 <- sample(1:nrow(filtered_data19_months), 
                          0.7 * nrow(filtered_data19_months))
train_indices20 <- sample(1:nrow(filtered_data20_months), 
                          0.7 * nrow(filtered_data20_months))
# train_indices21 <- sample(1:nrow(filtered_data21_months), 
#                           0.7 * nrow(filtered_data21_months))
# train_indices22 <- sample(1:nrow(filtered_data22_months), 
#                           0.7 * nrow(filtered_data22_months))
# train_indices23 <- sample(1:nrow(filtered_data23_months), 
#                           0.7 * nrow(filtered_data23_months))
# train_indices24 <- sample(1:nrow(filtered_data24_months), 
#                           0.7 * nrow(filtered_data24_months))
train_data1 <- filtered_data1_slope[train_indices1, ]
train_data2 <- filtered_data2_slope[train_indices2, ]
train_data3 <- filtered_data3_slope[train_indices3, ]
train_data4 <- filtered_data4_slope[train_indices4, ]
# train_data5 <- filtered_data5_slope[train_indices5, ]
# train_data6 <- filtered_data6_slope[train_indices6, ]
# train_data7 <- filtered_data7_slope[train_indices7, ]
# train_data8 <- filtered_data8_slope[train_indices8, ]
train_data9 <- filtered_data9_threshold[train_indices9, ]
train_data10 <- filtered_data10_threshold[train_indices10, ]
train_data11 <- filtered_data11_threshold[train_indices11, ]
train_data12 <- filtered_data12_threshold[train_indices12, ]
# train_data13 <- filtered_data13_threshold[train_indices13, ]
# train_data14 <- filtered_data14_threshold[train_indices14, ]
# train_data15 <- filtered_data15_threshold[train_indices15, ]
# train_data16 <- filtered_data16_threshold[train_indices16, ]
train_data17 <- filtered_data17_months[train_indices17, ]
train_data18 <- filtered_data18_months[train_indices18, ]
train_data19 <- filtered_data19_months[train_indices19, ]
train_data20 <- filtered_data20_months[train_indices20, ]
# train_data21 <- filtered_data21_months[train_indices21, ]
# train_data22 <- filtered_data22_months[train_indices22, ]
# train_data23 <- filtered_data23_months[train_indices23, ]
# train_data24 <- filtered_data24_months[train_indices24, ]
test_data1 <- filtered_data1_slope[-train_indices1, ]
test_data2 <- filtered_data2_slope[-train_indices2, ]
test_data3 <- filtered_data3_slope[-train_indices3, ]
test_data4 <- filtered_data4_slope[-train_indices4, ]
# test_data5 <- filtered_data5_slope[-train_indices5, ]
# test_data6 <- filtered_data6_slope[-train_indices6, ]
# test_data7 <- filtered_data7_slope[-train_indices7, ]
# test_data8 <- filtered_data8_slope[-train_indices8, ]
test_data9 <- filtered_data9_threshold[-train_indices9, ]
test_data10 <- filtered_data10_threshold[-train_indices10, ]
test_data11 <- filtered_data11_threshold[-train_indices11, ]
test_data12 <- filtered_data12_threshold[-train_indices12, ]
# test_data13 <- filtered_data13_threshold[-train_indices13, ]
# test_data14 <- filtered_data14_threshold[-train_indices14, ]
# test_data15 <- filtered_data15_threshold[-train_indices15, ]
# test_data16 <- filtered_data16_threshold[-train_indices16, ]
test_data17 <- filtered_data17_months[-train_indices17, ]
test_data18 <- filtered_data18_months[-train_indices18, ]
test_data19 <- filtered_data19_months[-train_indices19, ]
test_data20 <- filtered_data20_months[-train_indices20, ]
# test_data21 <- filtered_data21_months[-train_indices21, ]
# test_data22 <- filtered_data22_months[-train_indices22, ]
# test_data23 <- filtered_data23_months[-train_indices23, ]
# test_data24 <- filtered_data24_months[-train_indices24, ]

Fit models

print(rf1$time)
   user  system elapsed 
  47.47    4.10  108.93 
print(rf2$time)
   user  system elapsed 
  18.67    0.86   40.32 
print(rf3$time)
   user  system elapsed 
  50.26    4.05  104.86 
print(rf4$time)
   user  system elapsed 
  18.94    0.89   39.73 
# print(rf5$time)
# print(rf6$time)
# print(rf7$time)
# print(rf8$time)
print(rf9$time)
   user  system elapsed 
  26.03    1.33   56.89 
print(rf10$time)
   user  system elapsed 
  18.11    0.69   38.53 
print(rf11$time)
   user  system elapsed 
  31.43    2.08   60.69 
print(rf12$time)
   user  system elapsed 
  17.40    0.54   37.14 
# print(rf13$time)
# print(rf14$time)
# print(rf15$time)
# print(rf16$time)
print(rf17$time)
   user  system elapsed 
  41.08    3.07   82.16 
print(rf18$time)
   user  system elapsed 
  20.60    0.69   43.55 
print(rf19$time)
   user  system elapsed 
  33.16    2.61   68.91 
print(rf20$time)
   user  system elapsed 
  67.74    1.56   68.28 
# print(rf21$time)
# print(rf22$time)
# print(rf23$time)
# print(rf24$time)

Predictions

Confusion matrices

# 1: Slope method, no previous validation, GPS points
confusionMatrix(predictions_rf1, test_data1$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q 1067  344  184   13
         R  522 2533  326   69
         S  207  172  848   16
         T    8   27   10   91

Overall Statistics
                                          
               Accuracy : 0.7051          
                 95% CI : (0.6938, 0.7163)
    No Information Rate : 0.4779          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.5336          
                                          
 Mcnemar's Test P-Value : < 2.2e-16       

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.5915   0.8235   0.6199  0.48148
Specificity            0.8832   0.7272   0.9221  0.99280
Pos Pred Value         0.6636   0.7342   0.6822  0.66912
Neg Pred Value         0.8474   0.8182   0.8999  0.98445
Prevalence             0.2803   0.4779   0.2125  0.02936
Detection Rate         0.1658   0.3935   0.1317  0.01414
Detection Prevalence   0.2498   0.5360   0.1931  0.02113
Balanced Accuracy      0.7373   0.7753   0.7710  0.73714
# 2: Slope method, no previous validation, GPS diff points
confusionMatrix(predictions_rf2, test_data2$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   2   0   1   1
         R  70 942  66  63
         S   1   0   0   0
         T   0   5   1   2

Overall Statistics
                                          
               Accuracy : 0.8198          
                 95% CI : (0.7963, 0.8415)
    No Information Rate : 0.8206          
    P-Value [Acc > NIR] : 0.549           
                                          
                  Kappa : 0.041           
                                          
 Mcnemar's Test P-Value : <2e-16          

Statistics by Class:

                     Class: Q Class: R  Class: S Class: T
Sensitivity          0.027397  0.99472 0.0000000 0.030303
Specificity          0.998150  0.03865 0.9990792 0.994485
Pos Pred Value       0.500000  0.82559 0.0000000 0.250000
Neg Pred Value       0.938261  0.61538 0.9410234 0.944154
Prevalence           0.063258  0.82062 0.0589255 0.057192
Detection Rate       0.001733  0.81629 0.0000000 0.001733
Detection Prevalence 0.003466  0.98873 0.0008666 0.006932
Balanced Accuracy    0.512774  0.51668 0.4995396 0.512394
# 3: Slope method, rough validation, GPS points
confusionMatrix(predictions_rf3, test_data3$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q  979  336  192    1
         R  497 2441  249    2
         S  184  133  758    0
         T    0    0    0  126

Overall Statistics
                                         
               Accuracy : 0.7297         
                 95% CI : (0.7182, 0.741)
    No Information Rate : 0.4934         
    P-Value [Acc > NIR] : < 2.2e-16      
                                         
                  Kappa : 0.5667         
                                         
 Mcnemar's Test P-Value : NA             

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.5898   0.8388   0.6322  0.97674
Specificity            0.8752   0.7497   0.9325  1.00000
Pos Pred Value         0.6492   0.7654   0.7051  1.00000
Neg Pred Value         0.8449   0.8269   0.9086  0.99948
Prevalence             0.2815   0.4934   0.2033  0.02187
Detection Rate         0.1660   0.4139   0.1285  0.02136
Detection Prevalence   0.2557   0.5407   0.1823  0.02136
Balanced Accuracy      0.7325   0.7942   0.7824  0.98837
# 4: Slope method, rough validation, GPS diff points
confusionMatrix(predictions_rf4, test_data4$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   0   0   0   0
         R  67 876  50   2
         S   0   0   0   0
         T   0   0   1  20

Overall Statistics
                                          
               Accuracy : 0.8819          
                 95% CI : (0.8604, 0.9011)
    No Information Rate : 0.8622          
    P-Value [Acc > NIR] : 0.0359          
                                          
                  Kappa : 0.2388          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity           0.00000   1.0000   0.0000  0.90909
Specificity           1.00000   0.1500   1.0000  0.99899
Pos Pred Value            NaN   0.8804      NaN  0.95238
Neg Pred Value        0.93406   1.0000   0.9498  0.99799
Prevalence            0.06594   0.8622   0.0502  0.02165
Detection Rate        0.00000   0.8622   0.0000  0.01969
Detection Prevalence  0.00000   0.9793   0.0000  0.02067
Balanced Accuracy     0.50000   0.5750   0.5000  0.95404
# 5: Slope method, rough validation + refinement 10-90th, GPS points
# confusionMatrix(predictions_rf5, test_data5$EUNISa_1)
# 6: Slope method, rough validation + refinement 10-90th, GPS diff points
# confusionMatrix(predictions_rf6, test_data6$EUNISa_1)
# 7: Slope method, rough validation + refinement 20-80th, GPS points
# confusionMatrix(predictions_rf7, test_data7$EUNISa_1)
# 8: Slope method, rough validation + refinement 20-80th, GPS diff points
# confusionMatrix(predictions_rf8, test_data8$EUNISa_1)
# 9: Threshold method, no previous validation, GPS points
confusionMatrix(predictions_rf9, test_data9$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q  429  112   69    6
         R  223 1161  123   21
         S   84   67  322    2
         T    4    7    6   31

Overall Statistics
                                          
               Accuracy : 0.7285          
                 95% CI : (0.7112, 0.7453)
    No Information Rate : 0.5051          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.556           
                                          
 Mcnemar's Test P-Value : 6.417e-12       

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.5797   0.8619   0.6192  0.51667
Specificity            0.9030   0.7220   0.9287  0.99348
Pos Pred Value         0.6964   0.7598   0.6779  0.64583
Neg Pred Value         0.8484   0.8367   0.9097  0.98893
Prevalence             0.2775   0.5051   0.1950  0.02250
Detection Rate         0.1609   0.4353   0.1207  0.01162
Detection Prevalence   0.2310   0.5729   0.1781  0.01800
Balanced Accuracy      0.7413   0.7919   0.7740  0.75507
# 10: Threshold method, no previous validation, GPS diff points
confusionMatrix(predictions_rf10, test_data10$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   1   0   0   1
         R  31 361  29  20
         S   0   0   0   0
         T   2   1   0   0

Overall Statistics
                                          
               Accuracy : 0.8117          
                 95% CI : (0.7722, 0.8469)
    No Information Rate : 0.8117          
    P-Value [Acc > NIR] : 0.5291          
                                          
                  Kappa : 0.0429          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity          0.029412  0.99724  0.00000 0.000000
Specificity          0.997573  0.04762  1.00000 0.992941
Pos Pred Value       0.500000  0.81859      NaN 0.000000
Neg Pred Value       0.925676  0.80000  0.93498 0.952596
Prevalence           0.076233  0.81166  0.06502 0.047085
Detection Rate       0.002242  0.80942  0.00000 0.000000
Detection Prevalence 0.004484  0.98879  0.00000 0.006726
Balanced Accuracy    0.513492  0.52243  0.50000 0.496471
# 11: Threshold method, rough validation, GPS points
confusionMatrix(predictions_rf11, test_data11$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q  422  154   75    0
         R  174 1060   98    1
         S   72   71  298    0
         T    0    0    0   38

Overall Statistics
                                          
               Accuracy : 0.7381          
                 95% CI : (0.7203, 0.7554)
    No Information Rate : 0.5217          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.5717          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.6317   0.8249   0.6327  0.97436
Specificity            0.8724   0.7683   0.9282  1.00000
Pos Pred Value         0.6482   0.7952   0.6757  1.00000
Neg Pred Value         0.8642   0.8009   0.9144  0.99959
Prevalence             0.2712   0.5217   0.1912  0.01583
Detection Rate         0.1713   0.4304   0.1210  0.01543
Detection Prevalence   0.2643   0.5412   0.1790  0.01543
Balanced Accuracy      0.7521   0.7966   0.7805  0.98718
# 12: Threshold method, rough validation, GPS diff points
confusionMatrix(predictions_rf12, test_data12$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   0   0   0   0
         R  30 338  15   0
         S   0   0   0   0
         T   0   0   0   9

Overall Statistics
                                         
               Accuracy : 0.8852         
                 95% CI : (0.8494, 0.915)
    No Information Rate : 0.8622         
    P-Value [Acc > NIR] : 0.1044         
                                         
                  Kappa : 0.2689         
                                         
 Mcnemar's Test P-Value : NA             

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity           0.00000   1.0000  0.00000  1.00000
Specificity           1.00000   0.1667  1.00000  1.00000
Pos Pred Value            NaN   0.8825      NaN  1.00000
Neg Pred Value        0.92347   1.0000  0.96173  1.00000
Prevalence            0.07653   0.8622  0.03827  0.02296
Detection Rate        0.00000   0.8622  0.00000  0.02296
Detection Prevalence  0.00000   0.9770  0.00000  0.02296
Balanced Accuracy     0.50000   0.5833  0.50000  1.00000
# 13: Threshold method, rough validation + refinement 10-90th, GPS points
# confusionMatrix(predictions_rf13, test_data13$EUNISa_1)
# 14: Threshold method, rough validation + refinement 10-90th, GPS diff points
# confusionMatrix(predictions_rf14, test_data14$EUNISa_1)
# 15: Threshold method, rough validation + refinement 20-80th, GPS points
# confusionMatrix(predictions_rf15, test_data15$EUNISa_1)
# 16: Threshold method, rough validation + refinement 20-80th, GPS diff points
# confusionMatrix(predictions_rf16, test_data16$EUNISa_1)
# 17: Months method, no previous validation, GPS points
confusionMatrix(predictions_rf17, test_data17$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q 1011  254  116    8
         R  319 2257  236   62
         S  162  126  827   19
         T    7   16   13  109

Overall Statistics
                                          
               Accuracy : 0.7586          
                 95% CI : (0.7471, 0.7698)
    No Information Rate : 0.4787          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.6222          
                                          
 Mcnemar's Test P-Value : 1.689e-14       

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.6744   0.8507   0.6938  0.55051
Specificity            0.9065   0.7864   0.9294  0.99326
Pos Pred Value         0.7279   0.7853   0.7293  0.75172
Neg Pred Value         0.8825   0.8516   0.9172  0.98351
Prevalence             0.2705   0.4787   0.2151  0.03573
Detection Rate         0.1824   0.4073   0.1492  0.01967
Detection Prevalence   0.2506   0.5186   0.2046  0.02616
Balanced Accuracy      0.7905   0.8186   0.8116  0.77188
# 18: Months method, no previous validation, GPS diff points
confusionMatrix(predictions_rf18, test_data18$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   1   3   1   4
         R  75 815  57  46
         S   0   1   0   0
         T   3   6   1   9

Overall Statistics
                                         
               Accuracy : 0.8072         
                 95% CI : (0.7817, 0.831)
    No Information Rate : 0.8072         
    P-Value [Acc > NIR] : 0.519          
                                         
                  Kappa : 0.0986         
                                         
 Mcnemar's Test P-Value : <2e-16         

Statistics by Class:

                      Class: Q Class: R  Class: S Class: T
Sensitivity          0.0126582  0.98788 0.0000000 0.152542
Specificity          0.9915164  0.09645 0.9989616 0.989616
Pos Pred Value       0.1111111  0.82075 0.0000000 0.473684
Neg Pred Value       0.9230010  0.65517 0.9422135 0.950150
Prevalence           0.0772994  0.80724 0.0577299 0.057730
Detection Rate       0.0009785  0.79746 0.0000000 0.008806
Detection Prevalence 0.0088063  0.97162 0.0009785 0.018591
Balanced Accuracy    0.5020873  0.54216 0.4994808 0.571079
# 19: Months method, rough validation, GPS points
confusionMatrix(predictions_rf19, test_data19$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction    Q    R    S    T
         Q 1012  219   93    0
         R  271 2156  167    1
         S  116  112  762    0
         T    0    0    0  164

Overall Statistics
                                          
               Accuracy : 0.807           
                 95% CI : (0.7959, 0.8178)
    No Information Rate : 0.4902          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.697           
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity            0.7234   0.8669   0.7456  0.99394
Specificity            0.9151   0.8302   0.9437  1.00000
Pos Pred Value         0.7644   0.8308   0.7697  1.00000
Neg Pred Value         0.8968   0.8664   0.9363  0.99980
Prevalence             0.2758   0.4902   0.2015  0.03253
Detection Rate         0.1995   0.4250   0.1502  0.03233
Detection Prevalence   0.2610   0.5115   0.1952  0.03233
Balanced Accuracy      0.8192   0.8486   0.8447  0.99697
# 20: Months method, rough validation, GPS diff points
confusionMatrix(predictions_rf20, test_data20$EUNISa_1)
Confusion Matrix and Statistics

          Reference
Prediction   Q   R   S   T
         Q   0   1   0   0
         R  52 777  38  13
         S   0   0   0   0
         T   3   5   1   6

Overall Statistics
                                          
               Accuracy : 0.8739          
                 95% CI : (0.8504, 0.8949)
    No Information Rate : 0.8739          
    P-Value [Acc > NIR] : 0.525           
                                          
                  Kappa : 0.1074          
                                          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Q Class: R Class: S Class: T
Sensitivity          0.000000   0.9923  0.00000 0.315789
Specificity          0.998811   0.0885  1.00000 0.989738
Pos Pred Value       0.000000   0.8830      NaN 0.400000
Neg Pred Value       0.938547   0.6250  0.95647 0.985244
Prevalence           0.061384   0.8739  0.04353 0.021205
Detection Rate       0.000000   0.8672  0.00000 0.006696
Detection Prevalence 0.001116   0.9821  0.00000 0.016741
Balanced Accuracy    0.499405   0.5404  0.50000 0.652764
# 21: Months method, rough validation + refinement 10-90th, GPS points
# confusionMatrix(predictions_rf21, test_data21$EUNISa_1)
# 22: Months method, rough validation + refinement 10-90th, GPS diff points
# confusionMatrix(predictions_rf22, test_data22$EUNISa_1)
# 23: Months method, rough validation + refinement 20-80th, GPS points
# confusionMatrix(predictions_rf23, test_data23$EUNISa_1)
# 24: Months method, rough validation + refinement 20-80th, GPS diff points
# confusionMatrix(predictions_rf24, test_data24$EUNISa_1)

Plots

cm_rf19<- as.data.frame(as.table(confusionMatrix(predictions_rf19,
                                                 test_data19$EUNISa_1)))
colnames(cm_rf19) <- c("Prediction", "Reference", "Freq")
plot_cm_rf19 <- ggplot(cm_rf19, 
                       aes(x = Reference, y = Prediction, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), color = "black", size = 5) +
  scale_fill_gradient(low = "white", high = "steelblue") +
  labs(title = "Confusion Matrix", x = "Reference", y = "Prediction") +
  theme_minimal() + theme(legend.position = "none") +
  theme(axis.text = element_text(size = 12),
        axis.title = element_text(size = 14),
        plot.title = element_text(size = 16, face = "bold"))
plot_cm_rf19
ggsave(here("output", "figures", "RF", "plot_cm_rf19.jpeg"), plot_cm_rf19,
       dpi = 300, width = 3, height = 3)

Variable importance

Unconditional

Plots
plot(varimp_rf1$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf2$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf3$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf4$varimp, margin = c(10, 6, 2, 2))

# plot(varimp_rf5$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf6$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf7$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf8$varimp, margin = c(10, 6, 2, 2))
plot(varimp_rf9$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf10$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf11$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf12$varimp, margin = c(10, 6, 2, 2))

# plot(varimp_rf13$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf14$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf15$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf16$varimp, margin = c(10, 6, 2, 2))
plot(varimp_rf17$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf18$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf19$varimp, margin = c(10, 6, 2, 2))

plot(varimp_rf20$varimp, margin = c(10, 6, 2, 2))

# plot(varimp_rf21$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf22$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf23$varimp, margin = c(10, 6, 2, 2))
# plot(varimp_rf24$varimp, margin = c(10, 6, 2, 2))

Conditional (TBD)

Plots
plot(varimp-cond_rf1$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf1' no encontrado
g
plot(varimp-cond_rf2$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf2' no encontrado
g
plot(varimp-cond_rf3$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf3' no encontrado
g
plot(varimp-cond_rf4$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf4' no encontrado
g
# plot(varimp-cond_rf5$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf6$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf7$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf8$varimp, margin = c(9, 3, 2, 2))
plot(varimp-cond_rf9$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf9' no encontrado
g
plot(varimp-cond_rf10$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf10' no encontrado
g
plot(varimp-cond_rf11$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf11' no encontrado
g
plot(varimp-cond_rf12$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf12' no encontrado
g
# plot(varimp-cond_rf13$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf14$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf15$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf16$varimp, margin = c(9, 3, 2, 2))
plot(varimp-cond_rf17$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf17' no encontrado
g
plot(varimp-cond_rf18$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf18' no encontrado
g
plot(varimp-cond_rf19$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf19' no encontrado
g
plot(varimp-cond_rf20$varimp, margin = c(9, 3, 2, 2))
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'plot': objeto 'cond_rf20' no encontrado
g
# plot(varimp-cond_rf21$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf22$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf23$varimp, margin = c(9, 3, 2, 2))
# plot(varimp-cond_rf24$varimp, margin = c(9, 3, 2, 2))

ROC curves (TBD)

print(roc_data1$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data1' no encontrado
g
print(roc_data2$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data2' no encontrado
g
print(roc_data3$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data3' no encontrado
g
print(roc_data4$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data4' no encontrado
g
# print(roc_data5$time)
# print(roc_data6$time)
# print(roc_data7$time)
# print(roc_data8$time)
print(roc_data9$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data9' no encontrado
g
print(roc_data10$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data10' no encontrado
g
print(roc_data11$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data11' no encontrado
g
print(roc_data12$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data12' no encontrado
g
# print(roc_data13$time)
# print(roc_data14$time)
# print(roc_data15$time)
# print(roc_data16$time)
print(roc_data17$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data17' no encontrado
g
print(roc_data18$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data18' no encontrado
g
print(roc_data19$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data19' no encontrado
g
print(roc_data20$time)
G1;Error en h(simpleError(msg, call)): 
  error al evaluar el argumento 'x' al seleccionar un método para la función 'print': objeto 'roc_data20' no encontrado
g
# print(roc_data21$time)
# print(roc_data22$time)
# print(roc_data23$time)
# print(roc_data24$time)
save(roc_data1, 
     file = "objects/RF/rf1_rocdata_l1_slope_novalid_GPS_S2.Rdata")
G1;Error en save(roc_data1, file = "objects/RF/rf1_rocdata_l1_slope_novalid_GPS_S2.Rdata"): 
  object ‘roc_data1’ not found
g

Plots

roc1 <- ggplot(roc_data1$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc2 <- ggplot(roc_data2$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc3 <- ggplot(roc_data3$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc4 <- ggplot(roc_data4$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
# roc5 <- ggplot(roc_data5$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc6 <- ggplot(roc_data6$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc7 <- ggplot(roc_data7$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc8 <- ggplot(roc_data8$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
roc9 <- ggplot(roc_data9$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc10 <- ggplot(roc_data10$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc11 <- ggplot(roc_data11$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc12 <- ggplot(roc_data12$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
# roc13 <- ggplot(roc_data13$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc14 <- ggplot(roc_data14$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc15 <- ggplot(roc_data15$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc16 <- ggplot(roc_data16$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
roc17 <- ggplot(roc_data17$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc18 <- ggplot(roc_data18$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc19 <- ggplot(roc_data19$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
roc20 <- ggplot(roc_data20$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
  labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
       y = "True Positive Rate", color = "Class (AUC)") +
  theme_minimal() + theme(legend.position = "bottom")
# roc21 <- ggplot(roc_data21$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc22 <- ggplot(roc_data22$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc23 <- ggplot(roc_data23$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
# roc24 <- ggplot(roc_data24$roc, aes(x = FPR, y = TPR, color = Class)) +
#   geom_line(size = 1.2) + geom_abline(linetype = "dashed", color = "gray") +
#   labs(title = "Multiclass ROC Curves with AUC", x = "False Positive Rate",
#        y = "True Positive Rate", color = "Class (AUC)") +
#   theme_minimal() + theme(legend.position = "bottom")
roc1
roc2
roc3
roc4
# roc5
# roc6
# roc7
# roc8
roc9
roc10
roc11
roc12
# roc13
# roc14
# roc15
# roc16
roc17
roc18
roc19
roc20
# roc21
# roc22
# roc23
# roc24

With refinement

Refinement

Based on the 4 most important variables (except canopy_height). If the same type of variable is within the 4 most important for different indices, take next variable of different type. For rf19 (Months method, rough validation, GPS points), variables for refinement are: EVI_pos_value, SAVI_auc_mar_oct, SAVI_avg_value_03, NDVI_min.

distr_plot_percentiles <- function(data, y_vars, y_labels) {
  for (i in seq_along(y_vars)) {
    y_var <- y_vars[[i]]
    y_label <- y_labels[[i]]
    
    # Calculate percentiles per EUNISa_1 group
    percentiles <- data %>%
      group_by(EUNISa_1) %>%
      summarise(
        p10 = quantile(.data[[y_var]], 0.1, na.rm = TRUE),
        p90 = quantile(.data[[y_var]], 0.9, na.rm = TRUE),
        .groups = "drop"
      )
    
    # Join percentiles back to data
    data_flagged <- data %>%
      left_join(percentiles, by = "EUNISa_1") %>%
      mutate(outlier_flag = case_when(
        .data[[y_var]] < p10 ~ "low",
        .data[[y_var]] > p90 ~ "high",
        TRUE ~ "mid"
      ))
    
    # Filter and plot
    p <- ggplot(data = data_flagged %>%
                  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
                aes(x = EUNISa_1_descr, y = .data[[y_var]])) +
      geom_flat_violin(aes(fill = EUNISa_1_descr),
                       position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
      geom_point(aes(color = ifelse(outlier_flag == "mid",
                                    EUNISa_1_descr, "grey")),
                 position = position_jitter(width = 0.15), size = 1,
                 alpha = 0.6) +
      geom_boxplot(aes(fill = EUNISa_1_descr), width = 0.2, outlier.shape = NA,
                   alpha = 0.5) +
      stat_summary(fun = mean, geom = "point", shape = 20, size = 1) +
      stat_summary(fun.data = function(x) data.frame(y = max(x, na.rm = TRUE) +
                                                       0.1, label = length(x)),
                   geom = "text", aes(label = ..label..), vjust = 0.5) +
      labs(y = y_label, x = "EUNIS level 1") +
      scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
      guides(fill = FALSE, color = FALSE) +
      theme_bw() + coord_flip() +
      scale_color_manual(values = c(
        "Forests and other wooded land" = "#F8766D",
        "Grasslands" = "#7CAE00",
        "Heathlands, scrub and tundra" = "#00BFC4",
        "Wetlands" = "#C77CFF",
        "grey" = "grey"))
    
    print(p)
  }
}
distr_plot_percentiles(
  # GPS points after rough validation
  filtered_data19_months %>%
    # Join to get EUNIS 1 descriptions
    left_join(data_validation %>%
                select(PlotObservationID, EUNISa_1_descr)),
  c("EVI_pos_value", "SAVI_auc_mar_oct", "SAVI_avg_value_03", "NDVI_min"),
  c("EVI max value", "SAVI AUC", "AVerage SAVI value for March", "NDVI min value"))

Calculate percentiles:

percentiles_months <- 
  # GPS points after rough validation
  filtered_data19_months %>%
  group_by(EUNISa_1) %>%
  summarize(
    perc_10_SAVI_diff_pos_march_value = quantile(SAVI_diff_pos_march_value, 
                                            probs = 0.10, na.rm = T),
    perc_20_SAVI_diff_pos_march_value = quantile(SAVI_diff_pos_march_value, 
                                            probs = 0.20, na.rm = T),
    perc_80_SAVI_diff_pos_march_value = quantile(SAVI_diff_pos_march_value, 
                                            probs = 0.80, na.rm = T),
    perc_90_SAVI_diff_pos_march_value = quantile(SAVI_diff_pos_march_value,
                                            probs = 0.90, na.rm = T),
    perc_10_EVI_auc_mar_oct = quantile(EVI_auc_mar_oct, 
                                            probs = 0.10, na.rm = T),
    perc_20_EVI_auc_mar_oct = quantile(EVI_auc_mar_oct, 
                                            probs = 0.20, na.rm = T),
    perc_80_EVI_auc_mar_oct = quantile(EVI_auc_mar_oct, 
                                            probs = 0.80, na.rm = T),
    perc_90_EVI_auc_mar_oct = quantile(EVI_auc_mar_oct,
                                            probs = 0.90, na.rm = T),
    perc_10_SAVI_diff_pos_oct_doy = quantile(SAVI_diff_pos_oct_doy, 
                                            probs = 0.10, na.rm = T),
    perc_20_SAVI_diff_pos_oct_doy = quantile(SAVI_diff_pos_oct_doy, 
                                            probs = 0.20, na.rm = T),
    perc_80_SAVI_diff_pos_oct_doy = quantile(SAVI_diff_pos_oct_doy, 
                                            probs = 0.80, na.rm = T),
    perc_90_SAVI_diff_pos_oct_doy = quantile(SAVI_diff_pos_oct_doy,
                                            probs = 0.90, na.rm = T),
    perc_10_EVI_pos_value = quantile(EVI_pos_value, 
                                            probs = 0.10, na.rm = T),
    perc_20_EVI_pos_value = quantile(EVI_pos_value, 
                                            probs = 0.20, na.rm = T),
    perc_80_EVI_pos_value = quantile(EVI_pos_value, 
                                            probs = 0.80, na.rm = T),
    perc_90_EVI_pos_value = quantile(EVI_pos_value,
                                            probs = 0.90, na.rm = T)
            )

Get filtered data

# 21: Months method, rough validation + refinement 10-90th, GPS points
filtered_data21_months <- 
  # GPS points after rough validation
  filtered_data19_months %>%
  left_join(percentiles_months, by = "EUNISa_1") %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  dplyr::filter(
    (SAVI_diff_pos_march_value >= perc_10_SAVI_diff_pos_march_value &
       SAVI_diff_pos_march_value <= perc_90_SAVI_diff_pos_march_value) &
      (EVI_auc_mar_oct >= perc_10_EVI_auc_mar_oct &
         EVI_auc_mar_oct <= perc_90_EVI_auc_mar_oct) &
      (SAVI_diff_pos_oct_doy >= perc_10_SAVI_diff_pos_oct_doy &
         SAVI_diff_pos_oct_doy <= perc_90_SAVI_diff_pos_oct_doy) &
      (EVI_pos_value >= perc_10_EVI_pos_value &
         EVI_pos_value <= perc_90_EVI_pos_value)
    )

# 23: Months method, rough validation + refinement 20-80th, GPS points
filtered_data23_months <- 
  # GPS points after rough validation
  filtered_data19_months %>%
  left_join(percentiles_months, by = "EUNISa_1") %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  dplyr::filter(
    (SAVI_diff_pos_march_value >= perc_20_SAVI_diff_pos_march_value &
       SAVI_diff_pos_march_value <= perc_80_SAVI_diff_pos_march_value) &
      (EVI_auc_mar_oct >= perc_20_EVI_auc_mar_oct &
         EVI_auc_mar_oct <= perc_80_EVI_auc_mar_oct) &
      (SAVI_diff_pos_oct_doy >= perc_20_SAVI_diff_pos_oct_doy &
         SAVI_diff_pos_oct_doy <= perc_80_SAVI_diff_pos_oct_doy) &
      (EVI_pos_value >= perc_20_EVI_pos_value &
         EVI_pos_value <= perc_80_EVI_pos_value)
    )

N plots per category

bind_rows(
  filtered_data21_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data21_months") %>%
    select(data, Q, R, S, T),
  filtered_data23_months %>% count(EUNISa_1) %>%
    pivot_wider(names_from = EUNISa_1, values_from = n) %>% 
    mutate(data = "filtered_data23_months") %>%
    select(data, Q, R, S, T),
)

Split into training and test data sets

set.seed(123)
train_indices21 <- sample(1:nrow(filtered_data21_months), 
                         0.7 * nrow(filtered_data21_months))
train_indices23 <- sample(1:nrow(filtered_data23_months), 
                         0.7 * nrow(filtered_data23_months))
train_indices21 <- sample(1:nrow(filtered_data21_months), 
                         0.7 * nrow(filtered_data21_months))
train_indices23 <- sample(1:nrow(filtered_data23_months), 
                         0.7 * nrow(filtered_data23_months))
train_data21 <- filtered_data21_months[train_indices21, ]
train_data23 <- filtered_data23_months[train_indices23, ]

Fit models

print(rf21$time)
print(rf23$time)

Predictions

Confusion matrices

# 21: Months method, rough validation + refinement 10-90th, GPS points
confusionMatrix(predictions_rf21, test_data21$EUNISa_1)

# 23: Months method, rough validation + refinement 20-80th, GPS points
confusionMatrix(predictions_rf23, test_data23$EUNISa_1)

Plots

cm_rf21<- as.data.frame(as.table(confusionMatrix(predictions_rf21,
                                                 test_data21$EUNISa_1)))
colnames(cm_rf21) <- c("Prediction", "Reference", "Freq")
cm_rf23<- as.data.frame(as.table(confusionMatrix(predictions_rf23,
                                                 test_data23$EUNISa_1)))
colnames(cm_rf23) <- c("Prediction", "Reference", "Freq")
plot_cm_rf21 <- ggplot(cm_rf21, 
                       aes(x = Reference, y = Prediction, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), color = "black", size = 5) +
  scale_fill_gradient(low = "white", high = "steelblue") +
  labs(title = "Confusion Matrix", x = "Reference", y = "Prediction") +
  theme_minimal() + theme(legend.position = "none") +
  theme(axis.text = element_text(size = 12),
        axis.title = element_text(size = 14),
        plot.title = element_text(size = 16, face = "bold"))
plot_cm_rf23 <- ggplot(cm_rf23, 
                       aes(x = Reference, y = Prediction, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), color = "black", size = 5) +
  scale_fill_gradient(low = "white", high = "steelblue") +
  labs(title = "Confusion Matrix", x = "Reference", y = "Prediction") +
  theme_minimal() + theme(legend.position = "none") +
  theme(axis.text = element_text(size = 12),
        axis.title = element_text(size = 14),
        plot.title = element_text(size = 16, face = "bold"))
plot_cm_rf21
plot_cm_rf23
ggsave(here("output", "figures", "RF", "plot_cm_rf21.jpeg"), plot_cm_rf21,
       dpi = 300, width = 3, height = 3)
ggsave(here("output", "figures", "RF", "plot_cm_rf23.jpeg"), plot_cm_rf23,
       dpi = 300, width = 3, height = 3)

Variable importance

Unconditional

Plots
plot(varimp_rf21$varimp, margin = c(10, 6, 2, 2))
plot(varimp_rf23$varimp, margin = c(10, 6, 2, 2))

OLD, take from here

RF mmodels after training data refinement

6: GPS points, within 10-90th percentile

Refinement based on the variables: SAVI_pos_value, NDWI_min, NDMI_min, NDMI_max (later check variable importances well!).

Distribution plots:

distr_plot_percentiles <- function(data, y_vars, y_labels) {
  for (i in seq_along(y_vars)) {
    y_var <- y_vars[[i]]
    y_label <- y_labels[[i]]
    
    # Calculate percentiles per EUNISa_1 group
    percentiles <- data %>%
      group_by(EUNISa_1) %>%
      summarise(
        p10 = quantile(.data[[y_var]], 0.1, na.rm = TRUE),
        p90 = quantile(.data[[y_var]], 0.9, na.rm = TRUE),
        .groups = "drop"
      )
    
    # Join percentiles back to data
    data_flagged <- data %>%
      left_join(percentiles, by = "EUNISa_1") %>%
      mutate(outlier_flag = case_when(
        .data[[y_var]] < p10 ~ "low",
        .data[[y_var]] > p90 ~ "high",
        TRUE ~ "mid"
      ))
    
    # Filter and plot
    p <- ggplot(data = data_flagged %>%
                  filter(EUNISa_1 %in% c("T", "R", "S", "Q")),
                aes(x = EUNISa_1_descr, y = .data[[y_var]])) +
      geom_flat_violin(aes(fill = EUNISa_1_descr),
                       position = position_nudge(x = 0.2, y = 0), alpha = 0.8) +
      geom_point(aes(color = ifelse(outlier_flag == "mid",
                                    EUNISa_1_descr, "grey")),
                 position = position_jitter(width = 0.15), size = 1,
                 alpha = 0.6) +
      geom_boxplot(aes(fill = EUNISa_1_descr), width = 0.2, outlier.shape = NA,
                   alpha = 0.5) +
      stat_summary(fun = mean, geom = "point", shape = 20, size = 1) +
      stat_summary(fun.data = function(x) data.frame(y = max(x, na.rm = TRUE) +
                                                       0.1, label = length(x)),
                   geom = "text", aes(label = ..label..), vjust = 0.5) +
      labs(y = y_label, x = "EUNIS level 1") +
      scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
      guides(fill = FALSE, color = FALSE) +
      theme_bw() + coord_flip()
    
    print(p)
  }
}
distr_plot_percentiles(data_validation %>%
                         # Get GPS points after rough validation
             filter(PlotObservationID %in% filtered_data3$PlotObservationID),
           c("canopy_height", "SAVI_pos_value", "NDWI_min", "NDMI_min", 
             "NDMI_max"),
           c("canopy_height", "SAVI_pos_value", "NDWI_min", "NDMI_min", 
             "NDMI_max"))

So far not using canopy_height for refinement.

Calculate percentiles:

percentiles6 <- data_validation %>%
  # Get GPS points after rough validation
  filter(PlotObservationID %in% filtered_data3$PlotObservationID) %>%
  group_by(EUNISa_1) %>%
  summarize(
    percentile_10_SAVI_pos_value = quantile(SAVI_pos_value, 
                                            probs = 0.10, na.rm = T),
    percentile_20_SAVI_pos_value = quantile(SAVI_pos_value, 
                                            probs = 0.20, na.rm = T),
    percentile_80_SAVI_pos_value = quantile(SAVI_pos_value, 
                                            probs = 0.80, na.rm = T),
    percentile_90_SAVI_pos_value = quantile(SAVI_pos_value,
                                            probs = 0.90, na.rm = T),
    percentile_10_NDWI_min = quantile(NDWI_min, 
                                            probs = 0.10, na.rm = T),
    percentile_20_NDWI_min = quantile(NDWI_min, 
                                            probs = 0.20, na.rm = T),
    percentile_80_NDWI_min = quantile(NDWI_min, 
                                            probs = 0.80, na.rm = T),
    percentile_90_NDWI_min = quantile(NDWI_min,
                                            probs = 0.90, na.rm = T),
    percentile_10_NDMI_min = quantile(NDMI_min, 
                                            probs = 0.10, na.rm = T),
    percentile_20_NDMI_min = quantile(NDMI_min, 
                                            probs = 0.20, na.rm = T),
    percentile_80_NDMI_min = quantile(NDMI_min, 
                                            probs = 0.80, na.rm = T),
    percentile_90_NDMI_min = quantile(NDMI_min,
                                            probs = 0.90, na.rm = T),
    percentile_10_NDMI_max = quantile(NDMI_max, 
                                            probs = 0.10, na.rm = T),
    percentile_20_NDMI_max = quantile(NDMI_max, 
                                            probs = 0.20, na.rm = T),
    percentile_80_NDMI_max = quantile(NDMI_max, 
                                            probs = 0.80, na.rm = T),
    percentile_90_NDMI_max = quantile(NDMI_max,
                                            probs = 0.90, na.rm = T)
            )
filtered_data6 <- data_validation %>%
  # Get GPS points after rough validation
  filter(PlotObservationID %in% filtered_data3$PlotObservationID) %>%
  select(PlotObservationID, EUNISa_1, all_of(vars_RF)) %>%
  left_join(percentiles6, by = "EUNISa_1") %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  filter(
    (SAVI_pos_value >= percentile_10_SAVI_pos_value &
       SAVI_pos_value <= percentile_90_SAVI_pos_value) &
      (NDWI_min >= percentile_10_NDWI_min &
         NDWI_min <= percentile_90_NDWI_min) &
      (NDMI_min >= percentile_10_NDMI_min &
         NDMI_min <= percentile_90_NDMI_min) &
      (NDMI_max >= percentile_10_NDMI_max &
         NDMI_max <= percentile_90_NDMI_max)
    )

Split into training and test data sets.

set.seed(123)
train_indices6 <- sample(1:nrow(filtered_data6), 0.7 * nrow(filtered_data6))
train_data6 <- filtered_data6[train_indices6, ]
test_data6 <- filtered_data6[-train_indices6, ]

Number of points per category for filtered data:

filtered_data6 %>% count(EUNISa_1)

Confusion matrix:

confusionMatrix(predictions_rf6_S2, test_data6$EUNISa_1)

Variable Importance Plot

plot(varimp_rf6_S2$varimp, margin = c(9, 3, 2, 2))
plot(varimp_rf6_cond_S2$varimp, margin = c(9, 3, 2, 2))

ROC curves:

roc6 <- ggplot(roc_data6_S2$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) +
  geom_abline(linetype = "dashed", color = "gray") +
  labs(
    title = "Multiclass ROC Curves with AUC",
    x = "False Positive Rate",
    y = "True Positive Rate",
    color = "Class (AUC)"
  ) +
  theme_minimal() +
  theme(legend.position = "bottom")
roc6

7: GPS points, within 20-80th percentile

filtered_data7 <- data_validation %>%
  # Get GPS points after rough validation
  filter(PlotObservationID %in% filtered_data3$PlotObservationID) %>%
  select(PlotObservationID, EUNISa_1, all_of(vars_RF)) %>%
  left_join(percentiles6, by = "EUNISa_1") %>%
  mutate(EUNISa_1 = as.factor(EUNISa_1)) %>%
  filter(
    (SAVI_pos_value >= percentile_20_SAVI_pos_value &
       SAVI_pos_value <= percentile_80_SAVI_pos_value) &
      (NDWI_min >= percentile_20_NDWI_min &
         NDWI_min <= percentile_80_NDWI_min) &
      (NDMI_min >= percentile_20_NDMI_min &
         NDMI_min <= percentile_80_NDMI_min) &
      (NDMI_max >= percentile_20_NDMI_max &
         NDMI_max <= percentile_80_NDMI_max)
    )

Split into training and test data sets.

set.seed(123)
train_indices7 <- sample(1:nrow(filtered_data7), 0.7 * nrow(filtered_data7))
train_data7 <- filtered_data7[train_indices7, ]
test_data7 <- filtered_data7[-train_indices7, ]

Number of points per category for filtered data:

filtered_data7 %>% count(EUNISa_1)

Confusion matrix:

confusionMatrix(predictions_rf7_S2, test_data7$EUNISa_1)

Variable Importance Plot

plot(varimp_rf7_S2$varimp, margin = c(9, 3, 2, 2))
plot(varimp_rf7_cond_S2$varimp, margin = c(9, 3, 2, 2))

ROC curves:

roc7 <- ggplot(roc_data7_S2$roc, aes(x = FPR, y = TPR, color = Class)) +
  geom_line(size = 1.2) +
  geom_abline(linetype = "dashed", color = "gray") +
  labs(
    title = "Multiclass ROC Curves with AUC",
    x = "False Positive Rate",
    y = "True Positive Rate",
    color = "Class (AUC)"
  ) +
  theme_minimal() +
  theme(legend.position = "bottom")
roc6

OLD: Plots rough validation

ggplot(data_validation%>%
         mutate(rules_broken = case_when(
           valid_1_count == 1 & valid_1_NDWI == "wrong" ~ "NDWI",
           valid_1_count == 1 & valid_1_CH == "wrong" ~ "CH",
           valid_1_count == 2 &
             valid_1_NDWI == "wrong" & valid_1_CH == "wrong"~ "NDWI + CH",
           TRUE ~ NA_character_
         )), 
       aes(x = valid_1_count, fill = rules_broken)) +
  geom_bar() + labs(x = "Number of broken rules")

Proportion of observations not validated (so far):

nrow(data_validation %>% dplyr::filter(valid_1_count > 0))/
  nrow(data_validation)

But be aware that there are still MANY missing RS data.

ggplot(data_validation %>%
         mutate(diff_GPS = if_else(
           Lctnmth != "Location with differential GPS" |
             is.na(Lctnmth), "no", "yes")), 
       aes(x = diff_GPS, fill = valid_1)) +
  geom_bar() + labs(x = "Differential GPS")
ggplot(data_validation %>%
         mutate(GPS = case_when(
           Lctnmth == "Location with differential GPS" ~ "yes",
           Lctnmth == "Location with GPS" ~ "yes",
           is.na(Lctnmth) ~ "no",
           TRUE ~ "no"
         )), 
       aes(x = GPS, fill = valid_1)) +
  geom_bar() + labs(x = "GPS")

OLD from here

Maps

Points GPS

# Load world boundaries
world <- ne_countries(scale = "medium", returnclass = "sf")

# Calculate the extent of the points
points_GPS_extent <- data_validation %>%
  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
  dplyr::filter(S2_data == T | Landsat_data == T ) %>%
  dplyr::filter(`Location method` == "Location with differential GPS" |
           `Location method` == "Location with GPS") %>%
  summarise(lon_min = min(Lon_updated, na.rm = TRUE),
            lon_max = max(Lon_updated, na.rm = TRUE),
            lat_min = min(Lat_updated, na.rm = TRUE),
            lat_max = max(Lat_updated, na.rm = TRUE))

# Add padding to the extent (adjust as needed)
padding <- 2  # Adjust padding to your preference
x_limits <- c(points_GPS_extent$lon_min - padding,
              points_GPS_extent$lon_max + padding)
y_limits <- c(points_GPS_extent$lat_min - padding,
              points_GPS_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = data_validation %>%
               dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
               dplyr::filter(S2_data == T | Landsat_data == T ) %>%
               dplyr::filter(`Location method` == "Location with differential GPS" |
           `Location method` == "Location with GPS"),
             aes(x = Lon_updated, y = Lat_updated, color = EUNISa_1),
             size = 1) +
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Number of GPS points by Country:

data_validation %>%
  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
  dplyr::filter(S2_data == T | Landsat_data == T ) %>%
  dplyr::filter(`Location method` == "Location with differential GPS" |
           `Location method` == "Location with GPS") %>%
  count(Country)

Points ReSurvey

# Calculate the extent of the points
points_resurvey_extent <- data_validation %>%
  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
  dplyr::filter(S2_data == T | Landsat_data == T ) %>%
  summarise(lon_min = min(Lon_updated, na.rm = TRUE),
            lon_max = max(Lon_updated, na.rm = TRUE),
            lat_min = min(Lat_updated, na.rm = TRUE),
            lat_max = max(Lat_updated, na.rm = TRUE))

# Add padding to the extent (adjust as needed)
padding <- 2  # Adjust padding to your preference
x_limits <- c(points_resurvey_extent$lon_min - padding,
              points_resurvey_extent$lon_max + padding)
y_limits <- c(points_resurvey_extent$lat_min - padding,
              points_resurvey_extent$lat_max + padding)

# Create the zoomed map
ggplot() +
  geom_sf(data = world, fill = "lightblue", color = "gray") +
  geom_point(data = data_validation %>%
               dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
               dplyr::filter(S2_data == T | Landsat_data == T ),
             aes(x = Lon_updated, y = Lat_updated, color = EUNISa_1),
             size = 1) +
  coord_sf(xlim = x_limits, ylim = y_limits) +
  theme_minimal()

Number of ReSurvey points by Country:

data_validation %>%
  dplyr::filter(EUNISa_1 %in% c("T", "R", "S", "Q")) %>%
  dplyr::filter(S2_data == T | Landsat_data == T ) %>%
  count(Country)

Cordillera data

AlpineGrasslands_indices <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrassland_Sentinel_Plot_Allyear_Allmetrics.csv")
AlpineGrasslands_phen <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrasslands_Phenology_SOS_EOS_Peak_NDVI_Amplitude.csv")
AlpineGrasslands_CH <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/AlpineGrasslands/AlpineGrasslands_CanopyHeight_1m.csv")
VegetationTypes_indices <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_Sentinel_Plot_AllYear_Allmetrics.csv")
VegetationTypes_phen <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_Phenology_SOS_EOS_Peak_NDVI_Amplitude.csv")
VegetationTypes_CH <- read_csv(
  "C:/Data/MOTIVATE/Cordillera/VegetationTypes/VegetationTypes_CanopyHeight_1m.csv")
AlpineGrasslands <- AlpineGrasslands_indices %>%
  select(-`system:index`, -.geo, -Localidad) %>%
  rename(Hábitat = "H�bitat") %>% 
  full_join(AlpineGrasslands_phen  %>%
              select(-`system:index`, -.geo, -Localidad) %>%
              rename(Hábitat = "H�bitat")) %>%
  full_join(AlpineGrasslands_CH  %>%
              select(-`system:index`, -.geo, -Localidad)) %>%
  select(-Date__year, - `Precisi�n`) %>%
  mutate(DATE = ymd(DATE)) %>%
  rename(ID = "Releve_num") %>%
  mutate(ID = as.character(ID)) %>%
  mutate(layer = "AlpineGrasslands")
VegetationTypes <- VegetationTypes_indices %>%
  select(-`system:index`, -.geo) %>%
  full_join(VegetationTypes_phen  %>%
              select(-`system:index`, -.geo)) %>%
  full_join(VegetationTypes_CH  %>%
              select(-`system:index`, -.geo)) %>%
  rename(Hábitat = "TYPE") %>%
  mutate(layer = "VegetationTypes")

Merge both datasets:

cordillera <- bind_rows(
  AlpineGrasslands %>% select(DATE, ID, starts_with("NDMI"),
                              starts_with("NDVI"), Hábitat, "EOS_DOY",
                              "Peak_DOY", "SOS_DOY", "Season_Length",
                              "canopy_height", "layer"),
  VegetationTypes %>% select(DATE, ID, starts_with("NDMI"),
                              starts_with("NDVI"), Hábitat, "EOS_DOY",
                              "Peak_DOY", "SOS_DOY", "Season_Length",
                              "canopy_height", "layer")
  ) %>%
  mutate(EUNISa_1 = case_when(
    Hábitat = str_detect(Hábitat, "Pastizal|Cervunal|grassland|meadow") ~ "R",
    Hábitat = str_detect(Hábitat, "forest") ~ "T",
    Hábitat = str_detect(Hábitat, "Scrub|scrub|Shrubland|shrubland|shrub|Heathland") ~ "S",
    Hábitat = str_detect(Hábitat, "Suelo|Scree|scree|cliff") ~ "U",
    Hábitat = is.na(Hábitat) ~ "R",
    TRUE ~ NA_character_),
    EUNISa_1_descr = case_when(
      EUNISa_1 == "R" ~ "Grasslands",
      EUNISa_1 == "T" ~ "Forests and other wooded land",
      EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISa_1 == "U" ~ "Inland habitats with no or little soil")
    )

NDVI, NDMI

distr_plot(cordillera,
           c("NDVI_max", "NDVI_p90", "NDVI_min", "NDVI_p10"), 
           c("NDVI max", "NDVI p90", "NDVI min", "NDVI p10"))
distr_plot(cordillera,
           c("NDMI_max", "NDMI_p90", "NDMI_min", "NDMI_p10"), 
           c("NDMI max", "NDMI p90", "NDMI min", "NDMI p10"))

Session info

sessionInfo()
LS0tDQp0aXRsZTogIlNjcmlwdCB0byB2YWxpZGF0ZSBwb2ludHMgaW4gUmVTdXJ2ZXkgZGF0YWJhc2UgdXNpbmcgUlMgZGF0YSAoUzIgb25seSkiDQpzdWJ0aXRsZTogIlZhbGlkYXRpb24gZG9uZSB3aXRoIEFMTCBwb2ludHMgKGFsbCBvYnNlcnZhdGlvbnMpIg0KYXV0aG9yOiAiQWxpY2lhIFZhbGTDqXMiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KVGhpcyBSIHNjcmlwdCBpcyB1c2VkIHRvIHZhbGlkYXRlIHRoZSBwb2ludHMgaW4gdGhlIFJlU3VydmV5IGRhdGFiYXNlIHVzaW5nIFJTIGluZGljYXRvcnMgKGluZGljZXMgKyBwaGVub2xvZ3kgKyBjYW5vcHkgaGVpZ2h0KS4NCg0KIyBMb2FkIGxpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShzZikNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkNCmxpYnJhcnkoZHRwbHlyKQ0KbGlicmFyeShsbWU0KQ0KbGlicmFyeShsbWVyVGVzdCkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShnZ2VmZmVjdHMpDQpsaWJyYXJ5KHBhcnR5KQ0KbGlicmFyeShwYXJ0eWtpdCkNCmxpYnJhcnkobW9yZXBhcnR5KQ0KbGlicmFyeShkb1BhcmFsbGVsKQ0KbGlicmFyeShzdHJ1Y2NoYW5nZSkNCmxpYnJhcnkoZ2dwYXJ0eSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KG1vcmVwYXJ0eSkNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KbGlicmFyeShwUk9DKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkocmxhbmcpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGJlZXByKQ0KbGlicmFyeShmb3JlYWNoKQ0KbGlicmFyeShwZXJtaW1wKQ0KbGlicmFyeSh5YXJkc3RpY2spDQpgYGANCg0KIyBEZWZpbmUgcHJpbnRhbGwgZnVuY3Rpb24NCg0KYGBge3J9DQpwcmludGFsbCA8LSBmdW5jdGlvbih0aWJibGUpIHsNCiAgcHJpbnQodGliYmxlLCB3aWR0aCA9IEluZikNCiAgfQ0KYGBgDQoNCiMgTG9hZCBnZW9tX2ZsYXRfdmlvbGluIHBsb3QNCg0KYGBge3J9DQpzb3VyY2UoImh0dHBzOi8vZ2lzdC5naXRodWJ1c2VyY29udGVudC5jb20vYmVubWFyd2ljay8yYTFiYjAxMzNmZjU2OGNiZTI4ZC9yYXcvZmI1M2JkOTcxMjFmN2Y5Y2U5NDc4MzdlZjFhNGM2NWE3M2JmZmIzZi9nZW9tX2ZsYXRfdmlvbGluLlIiKQ0KYGBgDQoNCiMgTG9hZCBwcmV2aW91c2x5IGNyZWF0ZWQgb2JqZWN0cw0KDQpgYGB7cn0NCiMgRGVmaW5lIHRoZSBmb2xkZXIgcGF0aA0KZm9sZGVyX3BhdGggPC0gaGVyZSgib2JqZWN0cyIsICJSRiIsICJTMiIpDQoNCiMgTGlzdCBhbGwgLlJEYXRhIG9yIC5yZGEgZmlsZXMgaW4gdGhlIGZvbGRlcg0KcmRhdGFfZmlsZXMgPC0gbGlzdC5maWxlcyhmb2xkZXJfcGF0aCwgZnVsbC5uYW1lcyA9IFRSVUUpDQoNCiMgTG9hZCBlYWNoIGZpbGUNCmxhcHBseShyZGF0YV9maWxlcywgbG9hZCwgZW52aXIgPSAuR2xvYmFsRW52KQ0KYGBgDQoNCiMgUmVhZCBkYXRhDQoNCmBgYHtyfQ0KZGF0YV92YWxpZGF0aW9uIDwtIHJlYWRfdHN2KGhlcmUoDQogICJkYXRhIiwgImNsZWFuIiwiZmluYWxfUlNfZGF0YV9iYW5kc19TMl9hbGxfMjAyNTA4MDQuY3N2IikpDQpgYGANCg0KTm8gcGFyc2luZyBpc3N1ZXMhDQoNCiMgU29tZSBkYXRhIG1hbmFnZW5lbXQNCg0KIyMgVE8tRE86IE1pc3NpbmcgZGF0YSBjaGVja3MNCg0KRG8gd2hlbiBhbGwgUlMgZGF0YSBpcyByZWFkeSENCg0KIyBEaXN0cmlidXRpb25zIGFsbCBiaW9yZWdpb25zDQoNCiMjIEluZGljZXMNCg0KYGBge3J9DQojIERlZmluZSBhIGZ1bmN0aW9uIHRvIGNyZWF0ZSBoaXN0b2dyYW1zDQpwbG90X2hpc3RvZ3JhbSA8LSBmdW5jdGlvbihkYXRhLCB4X3ZhciwgeF9sYWJlbCkgew0KICBnZ3Bsb3QoZGF0YSAlPiUNCiAgICAgICAgICAgZHBseXI6OmZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSksDQogICAgICAgICBhZXMoeCA9ICEhc3ltKHhfdmFyKSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSArDQogICAgbGFicyh4ID0geF9sYWJlbCwgeSA9ICJGcmVxdWVuY3kiKSArDQogICAgdGhlbWVfYncoKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyBEZWZpbmUgYSBmdW5jdGlvbiB0byBjcmVhdGUgcGxvdHMgd2l0aCB2aW9saW4gKyBib3hwbG90ICsgcG9pbnRzDQpkaXN0cl9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIHlfdmFycywgeV9sYWJlbHMpIHsNCiAgZm9yIChpIGluIHNlcV9hbG9uZyh5X3ZhcnMpKSB7DQogICAgeV92YXIgPC0geV92YXJzW1tpXV0NCiAgICB5X2xhYmVsIDwtIHlfbGFiZWxzW1tpXV0NCiAgICANCiAgICBwIDwtIGdncGxvdChkYXRhID0gZGF0YSAlPiUNCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgICAgICAgICAgIGFlcyh4ID0gRVVOSVNhXzFfZGVzY3IsIHkgPSAhIXN5bSh5X3ZhciksIGZpbGwgPSBFVU5JU2FfMV9kZXNjcikpICsNCiAgICAgIGdlb21fZmxhdF92aW9saW4ocG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44KSArDQogICAgICBnZW9tX3BvaW50KGFlcyh5ID0gISFzeW0oeV92YXIpLCBjb2xvciA9IEVVTklTYV8xX2Rlc2NyKSwNCiAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjE1KSwgc2l6ZSA9IDEsIGFscGhhID0gMC4yNSkgKw0KICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yLCBvdXRsaWVyLnNoYXBlID0gTkEsIGFscGhhID0gMC41KSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gInBvaW50Iiwgc2hhcGUgPSAyMCwgc2l6ZSA9IDEpICsNCiAgICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGZ1bmN0aW9uKHgpIGRhdGEuZnJhbWUoeSA9IG1heCh4KSArIDAuMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBsZW5ndGgoeCkpLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogICAgICBsYWJzKHkgPSB5X2xhYmVsLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogICAgICBndWlkZXMoZmlsbCA9IEZBTFNFLCBjb2xvciA9IEZBTFNFKSArDQogICAgICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQogICAgDQogICAgcHJpbnQocCkNCiAgfQ0KfQ0KYGBgDQoNClJhbmdlcyBvZiBtaW4gYW5kIG1heDoNCg0KYGBge3J9DQpyYW5nZShkYXRhX3ZhbGlkYXRpb24kTkRWSV9tYXgsIG5hLnJtID0gVCkgIyBORFZJX21heCA+IDEgKHNsaWdodGx5KQ0KcmFuZ2UoZGF0YV92YWxpZGF0aW9uJE5ETUlfbWF4LCBuYS5ybSA9IFQpICMgTkRNSV9tYXggPiAxIChzbGlnaHRseSkNCnJhbmdlKGRhdGFfdmFsaWRhdGlvbiRORFdJX21heCwgbmEucm0gPSBUKQ0KcmFuZ2UoZGF0YV92YWxpZGF0aW9uJFNBVklfbWF4LCBuYS5ybSA9IFQpDQpyYW5nZShkYXRhX3ZhbGlkYXRpb24kRVZJX21heCwgbmEucm0gPSBUKSAjIEVWSV9tYXggPiAxIChzbGlnaHRseSkNCnJhbmdlKGRhdGFfdmFsaWRhdGlvbiRORFZJX21pbiwgbmEucm0gPSBUKQ0KcmFuZ2UoZGF0YV92YWxpZGF0aW9uJE5ETUlfbWluLCBuYS5ybSA9IFQpDQpyYW5nZShkYXRhX3ZhbGlkYXRpb24kTkRXSV9taW4sIG5hLnJtID0gVCkgIyBORFdJX21pbiA8IC0xIChzbGlnaHRseSkNCnJhbmdlKGRhdGFfdmFsaWRhdGlvbiRTQVZJX21pbiwgbmEucm0gPSBUKQ0KcmFuZ2UoZGF0YV92YWxpZGF0aW9uJEVWSV9taW4sIG5hLnJtID0gVCkgIyBFVklfbWluIDwgLTEhDQpgYGANCg0KYGBge3J9DQpucm93KGRhdGFfdmFsaWRhdGlvbiAlPiUgZHBseXI6OmZpbHRlcihORFZJX21heCA+IDEpKQ0KbnJvdyhkYXRhX3ZhbGlkYXRpb24gJT4lIGRwbHlyOjpmaWx0ZXIoTkRNSV9tYXggPiAxKSkNCm5yb3coZGF0YV92YWxpZGF0aW9uICU+JSBkcGx5cjo6ZmlsdGVyKEVWSV9tYXggPiAxKSkNCm5yb3coZGF0YV92YWxpZGF0aW9uICU+JSBkcGx5cjo6ZmlsdGVyKE5EV0lfbWluIDwgLTEpKQ0KbnJvdyhkYXRhX3ZhbGlkYXRpb24gJT4lIGRwbHlyOjpmaWx0ZXIoRVZJX21pbiA8IC0xKSkNCmBgYA0KDQpIaXN0b2dyYW1zIHRvIGNoZWNrIHRoYXQgbWF4IGFuZCBtaW4gdmFsdWVzIGFyZSBvazoNCg0KYGBge3J9DQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX21heCIsICJORFZJIG1heCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORE1JX21heCIsICJORE1JIG1heCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFdJX21heCIsICJORFdJIG1heCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJTQVZJX21heCIsICJTQVZJIG1heCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfbWF4IiwgIkVWSSBtYXgiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9taW4iLCAiTkRWSSBtaW4iKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRNSV9taW4iLCAiTkRNSSBtaW4iKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRXSV9taW4iLCAiTkRXSSBtaW4iKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9taW4iLCAiU0FWSSBtaW4iKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX21pbiIsICJFVkkgbWluIikNCmBgYA0KDQpgYGB7cn0NCm5yb3coZGF0YV92YWxpZGF0aW9uICU+JQ0KICAgICAgIGRwbHlyOjpmaWx0ZXIoRVZJX21heCA+IDEgfCBFVklfbWF4IDwgLTEpKQ0KZGF0YV92YWxpZGF0aW9uICU+JQ0KICBkcGx5cjo6ZmlsdGVyKEVWSV9tYXggPiAxIHwgRVZJX21heCA8IC0xKSAlPiUNCiAgY291bnQoYmlvZ2VvLCB1bml0KQ0KYGBgDQoNCk1vc3QgRVZJIHZhbHVlcyBhcmUgb2shDQoNCkRpc3RyaWJ1dGlvbiBwbG90czoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRpc3RyX3Bsb3QoZGF0YV92YWxpZGF0aW9uICU+JSBkcGx5cjo6ZmlsdGVyKEVWSV9taW4gPiAtMC41KSwNCiAgICAgICAgICAgYygiTkRWSV9tYXgiLCAiRVZJX21heCIsICJTQVZJX21heCIsICJORE1JX21heCIsICJORFdJX21heCIsDQogICAgICAgICAgICAgIk5EVklfbWluIiwgIkVWSV9taW4iLCAiU0FWSV9taW4iLCAiTkRNSV9taW4iLCAiTkRXSV9taW4iKSwNCiAgICAgICAgICAgYygiTkRWSV9tYXgiLCAiRVZJX21heCIsICJTQVZJX21heCIsICJORE1JX21heCIsICJORFdJX21heCIsDQogICAgICAgICAgICAgIk5EVklfbWluIiwgIkVWSV9taW4iLCAiU0FWSV9taW4iLCAiTkRNSV9taW4iLCAiTkRXSV9taW4iKSkNCmBgYA0KDQojIyBDSA0KDQpgYGB7cn0NCmRpc3RyX3Bsb3QoZGF0YV92YWxpZGF0aW9uLCAiY2Fub3B5X2hlaWdodCIsICJDYW5vcHkgaGVpZ2h0IChtKSIpDQpgYGANCiANCiMjIyBTaG93IGhhYml0YXRzIHdpdGggQ0ggY2F0ZWdvcmllcw0KDQpgYGB7cn0NCmdncGxvdChkYXRhX3ZhbGlkYXRpb24gJT4lDQogICAgICAgICBtdXRhdGUoQ0hfY2F0ID0NCiAgICAgICAgICAgICAgICAgIGZhY3RvcigNCiAgICAgICAgICAgICAgICAgICAgY2FzZV93aGVuKGNhbm9weV9oZWlnaHQgPT0gMCB+ICIwIG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDAgJiBjYW5vcHlfaGVpZ2h0IDw9IDEgfiAiMC0xIG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDEgJiBjYW5vcHlfaGVpZ2h0IDw9MiB+ICIxLTIgbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYW5vcHlfaGVpZ2h0ID4gMiAmIGNhbm9weV9oZWlnaHQgPD01IH4gIjItNSBtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbm9weV9oZWlnaHQgPiA1ICYgY2Fub3B5X2hlaWdodCA8PTggfiAiNS04IG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Fub3B5X2hlaWdodCA+IDggfiAiPiA4IG0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXMubmEoY2Fub3B5X2hlaWdodCkgfiBOQV9jaGFyYWN0ZXJfKSwNCiAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygNCiAgICAgICAgICAgICAgICAgICAgICAiMCBtIiwgIjAtMSBtIiwgIjEtMiBtIiwgIjItNSBtIiwgIjUtOCBtIiwgIj4gOCBtIikpKSwNCiAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCBmaWxsID0gQ0hfY2F0KSkgKw0KICBnZW9tX2JhcigpICsgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9udW1iZXIoKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSkgKw0KICBsYWJzKHggPSAiRVVOSVMgbGV2ZWwgMSIsIGZpbGwgPSAiQ2Fub3B5IGhlaWdodCIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwgMC43NSksDQogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiKQ0KYGBgDQoNCiMjIyBTdGF0cyBwZXIgaGFiaXRhdCB0eXBlDQoNCmBgYHtyfQ0KZGF0YV92YWxpZGF0aW9uICU+JQ0KICBncm91cF9ieShFVU5JU2FfMV9kZXNjcikgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoY2Fub3B5X2hlaWdodCwgbGlzdCgNCiAgICBtZWFuID0gbWVhbiwNCiAgICBtZWRpYW4gPSBtZWRpYW4sDQogICAgc2QgPSBzZCwNCiAgICBtaW4gPSBtaW4sDQogICAgbWF4ID0gbWF4DQogICAgKSwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQojIyBQaGVub2xvZ3kNCg0KT25seSB1c2luZyBORFZJLSBhbmQgU0FWSS1iYXNlZCB2YWx1ZXMgc28gZmFyLg0KDQpNYXhpbXVtIE5EVkkgc2hvdWxkIGJlIGVxdWFsIHRvIHZhbHVlIGF0IHBlYWs/DQoNCmBgYHtyfQ0KbnJvdyhkYXRhX3ZhbGlkYXRpb24gJT4lIGRwbHlyOjpmaWx0ZXIoTkRWSV9wb3NfdmFsdWUgICE9IE5EVklfbWF4KSkNCmBgYA0KDQpOb3Qgc3VyZSB3aHkgdGhpcyBoYXBwZW5zLCBidXQgaW4gbW9zdCBjYXNlcyB0aGUgZGlmZmVyZW5jZSBpcyBzbWFsbCAoPCAtMC4xKS4gQW55d2F5LCB3ZSBzaG91bGQgdXNlIG9ubHkgb25lIG9mIHRoZXNlIHR3byBpbiB0aGUgUkYgbW9kZWxzLg0KDQpgYGB7cn0NCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfc29zX3Nsb3BlX2RveSIsICJORFZJX3Nvc19zbG9wZV9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9wb3NfZG95IiwgIk5EVklfcG9zX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX2Vvc19zbG9wZV9kb3kiLCAiTkRWSV9lb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9zb3Nfc2xvcGVfZG95IiwgIkVWSV9zb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9wb3NfZG95IiwgIkVWSV9wb3NfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9lb3Nfc2xvcGVfZG95IiwgIkVWSV9lb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfc29zX3Nsb3BlX2RveSIsICJTQVZJX3Nvc19zbG9wZV9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9wb3NfZG95IiwgIlNBVklfcG9zX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJTQVZJX2Vvc19zbG9wZV9kb3kiLCAiU0FWSV9lb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfc29zX3RocmVzaG9sZF9kb3kiLCAiTkRWSV9zb3NfdGhyZXNob2xkX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX3Bvc19kb3kiLCAiTkRWSV9wb3NfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfZW9zX3RocmVzaG9sZF9kb3kiLCAiTkRWSV9lb3NfdGhyZXNob2xkX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfc29zX3RocmVzaG9sZF9kb3kiLCAiRVZJX3Nvc190aHJlc2hvbGRfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9wb3NfZG95IiwgIkVWSV9wb3NfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9lb3NfdGhyZXNob2xkX2RveSIsICJFVklfZW9zX3RocmVzaG9sZF9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9zb3NfdGhyZXNob2xkX2RveSIsICJTQVZJX3Nvc190aHJlc2hvbGRfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfcG9zX2RveSIsICJTQVZJX3Bvc19kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9lb3NfdGhyZXNob2xkX2RveSIsICJTQVZJX2Vvc190aHJlc2hvbGRfZG95IikNCmBgYA0KDQpgYGB7cn0NCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfc29zX3Nsb3BlX3ZhbHVlIiwgIk5EVklfc29zX3Nsb3BlX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfcG9zX3ZhbHVlIiwgIk5EVklfcG9zX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfZW9zX3Nsb3BlX3ZhbHVlIiwgIk5EVklfZW9zX3Nsb3BlX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9zb3Nfc2xvcGVfdmFsdWUiLCAiRVZJX3Nvc19zbG9wZV92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfcG9zX3ZhbHVlIiwgIkVWSV9wb3NfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2Vvc19zbG9wZV92YWx1ZSIsICJFVklfZW9zX3Nsb3BlX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfc29zX3RocmVzaG9sZF92YWx1ZSIsICJORFZJX3Nvc190aHJlc2hvbGRfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9wb3NfdmFsdWUiLCAiTkRWSV9wb3NfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9lb3NfdGhyZXNob2xkX3ZhbHVlIiwgIk5EVklfZW9zX3RocmVzaG9sZF92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfc29zX3RocmVzaG9sZF92YWx1ZSIsICJFVklfc29zX3RocmVzaG9sZF92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfcG9zX3ZhbHVlIiwgIkVWSV9wb3NfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2Vvc190aHJlc2hvbGRfdmFsdWUiLCAiRVZJX2Vvc190aHJlc2hvbGRfdmFsdWUiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9zbG9wZV9nc2QiLCAiTkRWSV9zbG9wZV9nc2QiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX3Nsb3BlX2dzZCIsICJFVklfc2xvcGVfZ3NkIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfc2xvcGVfZ3NkIiwgIlNBVklfc2xvcGVfZ3NkIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAgICJORFZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAgICJFVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgICAiRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJTQVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAgICJORFZJX2RpZmZfcG9zX3Nvc19zbG9wZV9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2RpZmZfcG9zX3Nvc19zbG9wZV9kb3kiLA0KICAgICAgICAgICAgICAgIkVWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwgDQogICAgICAgICAgICAgICAiRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwgDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfdGhyZXNob2xkX2dzZCIsICJORFZJX3RocmVzaG9sZF9nc2QiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX3RocmVzaG9sZF9nc2QiLCAiRVZJX3RocmVzaG9sZF9nc2QiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV90aHJlc2hvbGRfZ3NkIiwgIlNBVklfdGhyZXNob2xkX2dzZCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfdmFsdWUiLA0KICAgICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgICAiRVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAgICJORFZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfdmFsdWUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfdmFsdWUiLA0KICAgICAgICAgICAgICAgIkVWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIlNBVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiLA0KICAgICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiRVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95IiwNCiAgICAgICAgICAgICAgICJFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSIsDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IiwNCiAgICAgICAgICAgICAgICJORFZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IikNCnBsb3RfaGlzdG9ncmFtKGRhdGFfdmFsaWRhdGlvbiwgIkVWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIsIA0KICAgICAgICAgICAgICAgIkVWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJTQVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IiwgDQogICAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIpDQpgYGANCg0KYGBge3J9DQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJORFZJX2F1Y19zbG9wZSIsICJORFZJX2F1Y19zbG9wZSIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfYXVjX3Nsb3BlIiwgIkVWSV9hdWNfc2xvcGUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiU0FWSV9hdWNfc2xvcGUiLCAiU0FWSV9hdWNfc2xvcGUiKQ0KcGxvdF9oaXN0b2dyYW0oZGF0YV92YWxpZGF0aW9uLCAiTkRWSV9hdWNfdGhyZXNob2xkIiwgIk5EVklfYXVjX3RocmVzaG9sZCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJFVklfYXVjX3RocmVzaG9sZCIsICJFVklfYXVjX3RocmVzaG9sZCIpDQpwbG90X2hpc3RvZ3JhbShkYXRhX3ZhbGlkYXRpb24sICJTQVZJX2F1Y190aHJlc2hvbGQiLCAiU0FWSV9hdWNfdGhyZXNob2xkIikNCmBgYA0KDQpgYGB7cn0NCmRpc3RyX3Bsb3QoZGF0YV92YWxpZGF0aW9uLA0KICAgICAgICAgICBjKCJORFZJX3Nvc19zbG9wZV92YWx1ZSIsIk5EVklfcG9zX3ZhbHVlIiwgIk5EVklfZW9zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAiRVZJX3Nvc19zbG9wZV92YWx1ZSIsIkVWSV9wb3NfdmFsdWUiLCAiRVZJX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfc29zX3Nsb3BlX3ZhbHVlIiwgIlNBVklfcG9zX3ZhbHVlIiwgIlNBVklfZW9zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAiTkRWSV9zb3Nfc2xvcGVfZG95IiwiTkRWSV9wb3NfZG95IiwgIk5EVklfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIkVWSV9zb3Nfc2xvcGVfZG95IiwiRVZJX3Bvc19kb3kiLCAiRVZJX2Vvc19zbG9wZV9kb3kiLA0KICAgICAgICAgICAgICJTQVZJX3Nvc19zbG9wZV9kb3kiLCAiU0FWSV9wb3NfZG95IiwgIlNBVklfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIk5EVklfc29zX3RocmVzaG9sZF92YWx1ZSIsIk5EVklfcG9zX3ZhbHVlIiwgIk5EVklfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIkVWSV9zb3NfdGhyZXNob2xkX3ZhbHVlIiwiRVZJX3Bvc192YWx1ZSIsICJFVklfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfc29zX3RocmVzaG9sZF92YWx1ZSIsICJTQVZJX3Bvc192YWx1ZSIsICJTQVZJX2Vvc190aHJlc2hvbGRfdmFsdWUiLA0KICAgICAgICAgICAgICJORFZJX3Nvc190aHJlc2hvbGRfZG95IiwiTkRWSV9wb3NfZG95IiwgIk5EVklfZW9zX3RocmVzaG9sZF9kb3kiLA0KICAgICAgICAgICAgICJFVklfc29zX3RocmVzaG9sZF9kb3kiLCJFVklfcG9zX2RveSIsICJFVklfZW9zX3RocmVzaG9sZF9kb3kiLA0KICAgICAgICAgICAgICJTQVZJX3Nvc190aHJlc2hvbGRfZG95IiwgIlNBVklfcG9zX2RveSIsICJTQVZJX2Vvc190aHJlc2hvbGRfZG95IiksDQogICAgICAgICAgIGMoIk5EVklfc29zX3Nsb3BlX3ZhbHVlIiwiTkRWSV9wb3NfdmFsdWUiLCAiTkRWSV9lb3Nfc2xvcGVfdmFsdWUiLA0KICAgICAgICAgICAgICJFVklfc29zX3Nsb3BlX3ZhbHVlIiwiRVZJX3Bvc192YWx1ZSIsICJFVklfZW9zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAiU0FWSV9zb3Nfc2xvcGVfdmFsdWUiLCAiU0FWSV9wb3NfdmFsdWUiLCAiU0FWSV9lb3Nfc2xvcGVfdmFsdWUiLA0KICAgICAgICAgICAgICJORFZJX3Nvc19zbG9wZV9kb3kiLCJORFZJX3Bvc19kb3kiLCAiTkRWSV9lb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiRVZJX3Nvc19zbG9wZV9kb3kiLCJFVklfcG9zX2RveSIsICJFVklfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIlNBVklfc29zX3Nsb3BlX2RveSIsICJTQVZJX3Bvc19kb3kiLCAiU0FWSV9lb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiTkRWSV9zb3NfdGhyZXNob2xkX3ZhbHVlIiwiTkRWSV9wb3NfdmFsdWUiLCAiTkRWSV9lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiRVZJX3Nvc190aHJlc2hvbGRfdmFsdWUiLCJFVklfcG9zX3ZhbHVlIiwgIkVWSV9lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiU0FWSV9zb3NfdGhyZXNob2xkX3ZhbHVlIiwgIlNBVklfcG9zX3ZhbHVlIiwgIlNBVklfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIk5EVklfc29zX3RocmVzaG9sZF9kb3kiLCJORFZJX3Bvc19kb3kiLCAiTkRWSV9lb3NfdGhyZXNob2xkX2RveSIsDQogICAgICAgICAgICAgIkVWSV9zb3NfdGhyZXNob2xkX2RveSIsIkVWSV9wb3NfZG95IiwgIkVWSV9lb3NfdGhyZXNob2xkX2RveSIsDQogICAgICAgICAgICAgIlNBVklfc29zX3RocmVzaG9sZF9kb3kiLCAiU0FWSV9wb3NfZG95IiwgIlNBVklfZW9zX3RocmVzaG9sZF9kb3kiKQ0KICAgICAgICAgICApDQpgYGANCg0KYGBge3J9DQpkaXN0cl9wbG90KGRhdGFfdmFsaWRhdGlvbiwNCiAgICAgICAgICAgYygiTkRWSV9zbG9wZV9nc2QiLCJFVklfc2xvcGVfZ3NkIiwgIlNBVklfc2xvcGVfZ3NkIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUiLCAiRVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiLCAiRVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IiwgIkVWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwgIkVWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwNCiAgICAgICAgICAgICAiTkRWSV90aHJlc2hvbGRfZ3NkIiwiRVZJX3RocmVzaG9sZF9nc2QiLCAiU0FWSV90aHJlc2hvbGRfZ3NkIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSIsICJFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiLA0KICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95IiwNCiAgICAgICAgICAgICAiTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIsICJFVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF9kb3kiLA0KICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IiksDQogICAgICAgICAgIGMoIk5EVklfc2xvcGVfZ3NkIiwiRVZJX3Nsb3BlX2dzZCIsICJTQVZJX3Nsb3BlX2dzZCIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3Nsb3BlX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUiLA0KICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiLA0KICAgICAgICAgICAgICJTQVZJX2RpZmZfcG9zX2Vvc19zbG9wZV92YWx1ZSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSIsICJFVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSIsICJFVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSIsDQogICAgICAgICAgICAgIk5EVklfdGhyZXNob2xkX2dzZCIsIkVWSV90aHJlc2hvbGRfZ3NkIiwgIlNBVklfdGhyZXNob2xkX2dzZCIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIsICJFVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsICJFVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIlNBVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiLCAiRVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95IiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSIsDQogICAgICAgICAgICAgIk5EVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF9kb3kiLCAiRVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IiwNCiAgICAgICAgICAgICAiU0FWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIpDQogICAgICAgICAgICkNCmBgYA0KDQpgYGB7cn0NCmRpc3RyX3Bsb3QoZGF0YV92YWxpZGF0aW9uLA0KICAgICAgICAgICBjKCJORFZJX2F1Y19zbG9wZSIsICJFVklfYXVjX3Nsb3BlIiwgIlNBVklfYXVjX3Nsb3BlIiwNCiAgICAgICAgICAgICAiTkRWSV9hdWNfdGhyZXNob2xkIiwgIkVWSV9hdWNfdGhyZXNob2xkIiwgIlNBVklfYXVjX3RocmVzaG9sZCIsDQogICAgICAgICAgICAgIk5EVklfYXVjX21hcl9vY3QiLCAiRVZJX2F1Y19tYXJfb2N0IiwgIlNBVklfYXVjX21hcl9vY3QiKSwNCiAgICAgICAgICAgICBjKCJORFZJX2F1Y19zbG9wZSIsICJFVklfYXVjX3Nsb3BlIiwgIlNBVklfYXVjX3Nsb3BlIiwNCiAgICAgICAgICAgICAgICJORFZJX2F1Y190aHJlc2hvbGQiLCAiRVZJX2F1Y190aHJlc2hvbGQiLCAiU0FWSV9hdWNfdGhyZXNob2xkIiwNCiAgICAgICAgICAgICAgICJORFZJX2F1Y19tYXJfb2N0IiwgIkVWSV9hdWNfbWFyX29jdCIsICJTQVZJX2F1Y19tYXJfb2N0IikpDQpgYGANCg0KIyBUQkQ6IERpc3RyaWJ1dGlvbnMgcGVyIGJpb3JlZ2lvbg0KDQpgYGB7cn0NCiMgRGVmaW5lIGEgZnVuY3Rpb24gdG8gY3JlYXRlIHBsb3RzIHdpdGggdmlvbGluICsgYm94cGxvdCArIHBvaW50cw0KZGlzdHJfcGxvdF9iaW9nZW8gPC0gZnVuY3Rpb24oZGF0YSwgeV92YXJzLCB5X2xhYmVscykgew0KICBwbG90cyA8LSBsaXN0KCkNCiAgDQogIGZvciAoaSBpbiBzZXFfYWxvbmcoeV92YXJzKSkgew0KICAgIHlfdmFyIDwtIHlfdmFyc1tbaV1dDQogICAgeV9sYWJlbCA8LSB5X2xhYmVsc1tbaV1dDQogICAgDQogICAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEgJT4lDQogICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKSwNCiAgICAgICAgICAgICAgICBhZXMoeCA9IEVVTklTYV8xX2Rlc2NyLCB5ID0gISFzeW0oeV92YXIpLCBmaWxsID0gRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICBnZW9tX2ZsYXRfdmlvbGluKHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IDAuMiwgeSA9IDApLCBhbHBoYSA9IDAuOCkgKw0KICAgICAgZ2VvbV9wb2ludChhZXMoeSA9ICEhc3ltKHlfdmFyKSwgY29sb3IgPSBFVU5JU2FfMV9kZXNjciksDQogICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSksIHNpemUgPSAxLCBhbHBoYSA9IDAuMjUpICsNCiAgICAgIGdlb21fYm94cGxvdCh3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNSkgKw0KICAgICAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gMjAsIHNpemUgPSAxKSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHkgPSBtYXgoeCkgKyAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gbGVuZ3RoKHgpKSwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gInRleHQiLCBhZXMobGFiZWwgPSAuLmxhYmVsLi4pLCB2anVzdCA9IDAuNSkgKw0KICAgICAgbGFicyh5ID0geV9sYWJlbCwgeCA9ICJFVU5JU2FfMV9kZXNjciIpICsNCiAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxNSkpICsNCiAgICAgIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsNCiAgICAgIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkgKyBmYWNldF93cmFwKH4gYmlvZ2VvKQ0KICAgIA0KICAgIHBsb3RzW1t5X3Zhcl1dIDwtIHANCiAgfQ0KICANCiAgcmV0dXJuKHBsb3RzKQ0KfQ0KYGBgDQoNCiMjIEluZGljZXMNCg0KRGlzdHJpYnV0aW9uIHBsb3RzOg0KDQojIyBDSA0KDQpgYGB7cn0NCmRpc3RyX3Bsb3RfYmlvZ2VvKGRhdGFfdmFsaWRhdGlvbiwgImNhbm9weV9oZWlnaHQiLCAiQ2Fub3B5IGhlaWdodCAobSkiKQ0KYGBgDQoNCiMjIFBoZW5vbG9neQ0KDQojIERlZmluZSBmdW5jdGlvbnMgZm9yIFJGIG1vZGVscw0KDQojIyBGdW5jdGlvbiBmb3IgZml0dGluZyBSRiBtb2RlbHMNCg0KUkYgbW9kZWxzIGZpdHRlZCB1c2luZyB0aGUgY29uZGl0aW9uYWwgaW5mZXJlbmNlIHZlcnNpb24gb2YgcmFuZG9tIGZvcmVzdCAoZmlyc3QgY2ZvcmVzdCBpbiBwYWNrYWdlIHBhcnR5LCBub3cgZmFzdGNmb3Jlc3QgaW4gcGFja2FnZSBtb3JlcGFydHkpLiBTdWdnZXN0ZWQgaWYgdGhlIGRhdGEgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLiBDZm9yZXN0IGlzIG1vcmUgc3RhYmxlIGluIGRlcml2aW5nIHZhcmlhYmxlIGltcG9ydGFuY2UgdmFsdWVzIGluIHRoZSBwcmVzZW5jZSBvZiBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMsIHRodXMgcHJvdmlkaW5nIGJldHRlciBhY2N1cmFjeSBpbiBjYWxjdWxhdGluZyB2YXJpYWJsZSBpbXBvcnRhbmNlIChyZWYgYmVsb3cpLg0KDQpIb3Rob3JuLCBULiwgSG9ybmlrLCBLLiBhbmQgWmVpbGVpcywgQS4gKDIwMDYpIFVuYmlhc2VkIFJlY3Vyc2l2ZSBQb3J0aW9uaW5nOiBBIENvbmRpdGlvbmFsIEluZmVyZW5jZSBGcmFtZXdvcmsuIEpvdXJuYWwgb2YgQ29tcHV0YXRpb25hbCBhbmQgR3JhcGhpY2FsIFN0YXRpc3RpY3MsIDE1LCA2NTEtDQo2NzQuIGh0dHA6Ly9keC5kb2kub3JnLzEwLjExOTgvMTA2MTg2MDA2WDEzMzkzMw0KDQpDaG9vc2UgdGhlIGh5cGVycGFyYW1ldGVyIG10cnkgYmFzZWQgb24gdGhlIHNxdWFyZSByb290IG9mIHRoZSBudW1iZXIgb2YgcHJlZGljdG9yIHZhcmlhYmxlczoNCg0KSGFzdGllLCBULiwgVGlic2hpcmFuaSwgUi4sICYgRnJpZWRtYW4sIEouICgyMDA5KS4gVGhlIGVsZW1lbnRzIG9mIHN0YXRpc3RpY2FsDQpsZWFybmluZzogRGF0YSBtaW5pbmcsIGluZmVyZW5jZSwgYW5kIHByZWRpY3Rpb24uIFNwcmluZ2VyIFNjaWVuY2UgJg0KQnVzaW5lc3MgTWVkaWEuDQoNCk1heWJlIFRPX0RPOg0KV2UgdmFyaWF0ZWQgbnRyZWUgZnJvbSA1MCB0byA4MDAgaW4gc3RlcHMgb2YgNTAsIGxlYXZpbmcgbXRyeSBjb25zdGFudCBhdCAyLiBUaXMgcGFyYW1ldGVyIHZhcmlhdGlvbiBzaG93ZWQgdGhhdCBudHJlZT01MDAgd2FzIG9wdGltYWwsIHdoaWxlIGhpZ2hlciBudHJlZSBsZWQgdG8gbm8gZnVydGhlciBtb2RlbCBpbXByb3ZlbWVudCAoU3VwcGxlbWVudGFyeSBGaWcuIFMxMCkuIFN1YnNlcXVlbnRseSwgdGhlIGh5cGVycGFyYW1ldGVyIG10cnkgd2FzIHZhcmllZCBmcm9tIDIgdG8gOCB3aXRoIGNvbnN0YW50IG50cmVlPTUwMC4gSGVyZSwgbXRyeT0zIGxlZCB0byB0aGUgYmVzdCByZXN1bHRzIGluIGFsbW9zdCBhbGwgY2FzZXMgKFN1cHBsZW1lbnRhcnkgRmlnLiBTMTEpLiBDb25zZXF1ZW50bHksIHdlIGNob3NlIG50cmVlPTUwMCBhbmQgbXRyeT0zIGZvciBvdXIgbWFpbiBhbmFseXNpcyBhY3Jvc3MgYWxsIHN0dWR5IHNpdGVzLg0KDQpEZWZpbmUgYSBmdW5jdGlvbiB0byBydW4gZmFzdGNmb3Jlc3QgbW9kZWxzOg0KDQpgYGB7cn0NCnJ1bl9yZiA8LSBmdW5jdGlvbih2YXJzX1JGLCB0cmFpbl9kYXRhLCByZXNwb25zZV92YXIsIG50cmVlID0gNTAwKSANCiAgew0KICANCiAgIyBEZXRlY3QgYW5kIHJlZ2lzdGVyIGF2YWlsYWJsZSBjb3JlcyAobGVhdmUgb25lIGZyZWUpDQogIG5fY29yZXMgPC0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAxDQogIGNsIDwtIG1ha2VDbHVzdGVyKG5fY29yZXMpDQogIHJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCiAgDQogIHRyYWluX25hbWUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKHRyYWluX2RhdGEpKQ0KICANCiAgIyBFeHBvcnQgbmVjZXNzYXJ5IHZhcmlhYmxlcyB0byB0aGUgY2x1c3Rlcg0KICBjbHVzdGVyRXhwb3J0KGNsLCB2YXJsaXN0ID0gYygidmFyc19SRiIsICJ0cmFpbl9kYXRhIiwgInJlc3BvbnNlX3ZhciIpLA0KICAgICAgICAgICAgICAgIGVudmlyID0gZW52aXJvbm1lbnQoKSkNCg0KICANCiAgIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5DQogIHNldC5zZWVkKDEyMykNCiAgDQogICMgTWVhc3VyZSBleGVjdXRpb24gdGltZQ0KICBleGVjdXRpb25fdGltZSA8LSBzeXN0ZW0udGltZSh7DQogICAgcmZfbW9kZWwgPC0gZmFzdGNmb3Jlc3QoDQogICAgICBmb3JtdWxhID0gcmVmb3JtdWxhdGUodmFyc19SRiwgcmVzcG9uc2UgPSByZXNwb25zZV92YXIpLA0KICAgICAgZGF0YSA9IHRyYWluX2RhdGEsDQogICAgICBjb250cm9scyA9IHBhcnR5OjpjZm9yZXN0X2NvbnRyb2woDQogICAgICAgIG10cnkgPSByb3VuZChzcXJ0KGxlbmd0aCh2YXJzX1JGKSkpLA0KICAgICAgICBudHJlZSA9IG50cmVlDQogICAgICApLA0KICAgICAgcGFyYWxsZWwgPSBUUlVFDQogICAgKQ0KICB9KQ0KICANCiAgIyBTdG9wIHRoZSBjbHVzdGVyDQogIHN0b3BDbHVzdGVyKGNsKQ0KICANCiAgIyBSZXR1cm4gYm90aCB0aGUgbW9kZWwgYW5kIGV4ZWN1dGlvbiB0aW1lDQogIGxpc3QobW9kZWwgPSByZl9tb2RlbCwgdGltZSA9IGV4ZWN1dGlvbl90aW1lKQ0KfQ0KYGBgDQoNCiMjIEZ1bmN0aW9uIHRvIGNvbXB1dGUgdmFyaWFibGUgaW1wb3J0YW5jZQ0KDQpgYGB7cn0NCiMgY29tcHV0ZV92YXJpbXAgPC0gZnVuY3Rpb24obW9kZWwsIG5wZXJtID0gMTAwLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2NvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkgLSAxKSB7DQojICAgIyBTZXQgdXAgcGFyYWxsZWwgYmFja2VuZA0KIyAgIGNsIDwtIG1ha2VDbHVzdGVyKG5fY29yZXMpDQojICAgcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQ0KIyAgIA0KIyAgICMgTWVhc3VyZSBleGVjdXRpb24gdGltZQ0KIyAgIGV4ZWN1dGlvbl90aW1lIDwtIHN5c3RlbS50aW1lKHsNCiMgICAgIHZhcmltcF9saXN0IDwtIGZvcmVhY2goaSA9IDE6bnBlcm0sIC5jb21iaW5lID0gJysnLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnBhY2thZ2VzID0gInBhcnR5IikgJWRvcGFyJSB7DQojICAgICAgIHZhcmltcChtb2RlbCwgY29uZGl0aW9uYWwgPSBGQUxTRSwgbnBlcm0gPSAxKQ0KIyAgICAgfQ0KIyAgIH0pDQojICAgDQojICAgc3RvcENsdXN0ZXIoY2wpDQojICAgDQojICAgIyBBdmVyYWdlIHRoZSByZXN1bHRzDQojICAgdmFyaW1wX2F2ZyA8LSB2YXJpbXBfbGlzdCAvIG5wZXJtDQojICAgDQojICAgcmV0dXJuKGxpc3QodmFyaW1wID0gdmFyaW1wX2F2ZywgdGltZSA9IGV4ZWN1dGlvbl90aW1lKSkNCiMgfQ0KYGBgDQoNClVzaW5nIHBlcm1pbXAoKSBlbiBwZXJtaW1wIHBhY2thZ2U6DQpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcGVybWltcC92aWduZXR0ZXMvcGVybWltcC1wYWNrYWdlLmh0bWwjZm4xDQoNCmBgYHtyfQ0KY29tcHV0ZV92YXJpbXAgPC0gZnVuY3Rpb24obW9kZWwsIG5wZXJtID0gMTAwKSB7DQoNCiAgIyBNZWFzdXJlIGV4ZWN1dGlvbiB0aW1lDQogIGV4ZWN1dGlvbl90aW1lIDwtIHN5c3RlbS50aW1lKHsNCiAgICB2YXJpbXBfcmVzdWx0IDwtIHBlcm1pbXAobW9kZWwsIGNvbmRpdGlvbmFsID0gRkFMU0UsIHByb2dyZXNzQmFyID0gVFJVRSkNCiAgfSkNCg0KICByZXR1cm4obGlzdCh2YXJpbXAgPSB2YXJpbXBfcmVzdWx0LCB0aW1lID0gZXhlY3V0aW9uX3RpbWUpKQ0KfQ0KYGBgDQoNCiMjIEZ1bmN0aW9uIHRvIGNvbXB1dGUgQ09ORElUSU9OQUwgdmFyaWFibGUgaW1wb3J0YW5jZQ0KDQpgYGB7cn0NCmNvbXB1dGVfdmFyaW1wX2NvbmQgPC0gZnVuY3Rpb24obW9kZWwsIG5wZXJtID0gMTAwKSB7DQoNCiAgIyBNZWFzdXJlIGV4ZWN1dGlvbiB0aW1lDQogIGV4ZWN1dGlvbl90aW1lIDwtIHN5c3RlbS50aW1lKHsNCiAgICB2YXJpbXBfcmVzdWx0IDwtIHBlcm1pbXAobW9kZWwsIGNvbmRpdGlvbmFsID0gVFJVRSwgcHJvZ3Jlc3NCYXIgPSBUUlVFKQ0KICB9KQ0KDQogIHJldHVybihsaXN0KHZhcmltcCA9IHZhcmltcF9yZXN1bHQsIHRpbWUgPSBleGVjdXRpb25fdGltZSkpDQp9DQpgYGANCg0KIyMgRnVuY3Rpb24gdG8gY29tcHV0ZSBST0MgKGxldmVsIDEpDQoNCmBgYHtyfQ0KY29tcHV0ZV9yb2NfbGV2ZWwxIDwtIGZ1bmN0aW9uKG1vZGVsLCB0ZXN0X2RhdGEpIHsNCiAgIyBNZWFzdXJlIGV4ZWN1dGlvbiB0aW1lDQogIGV4ZWN1dGlvbl90aW1lIDwtIHN5c3RlbS50aW1lKHsNCiAgICAjIFN0ZXAgMTogUHJlZGljdCBwcm9iYWJpbGl0aWVzDQogICAgcHJvYmFiaWxpdGllcyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhLCB0eXBlID0gInByb2IiKQ0KICAgIA0KICAgICMgU3RlcCAyOiBDb252ZXJ0IGxpc3Qgb2YgbWF0cmljZXMgdG8gYSBwcm9wZXIgZGF0YSBmcmFtZQ0KICAgIHByb2JfbWF0cml4IDwtIHQoc2FwcGx5KHByb2JhYmlsaXRpZXMsIGFzLnZlY3RvcikpDQogICAgcHJvYl9kZiA8LSBhcy5kYXRhLmZyYW1lKHByb2JfbWF0cml4KQ0KICAgIGNvbG5hbWVzKHByb2JfZGYpIDwtIGMoIlEiLCAiUiIsICJTIiwgIlQiKQ0KICAgIA0KICAgICMgU3RlcCAzOiBQcmVwYXJlIGFjdHVhbCBjbGFzcyBsYWJlbHMNCiAgICBhY3R1YWwgPC0gZmFjdG9yKHRlc3RfZGF0YSRFVU5JU2FfMSwgbGV2ZWxzID0gYygiUSIsICJSIiwgIlMiLCAiVCIpKQ0KICAgIA0KICAgICMgU3RlcCA0OiBCaW5hcml6ZSBhY3R1YWwgbGFiZWxzDQogICAgYWN0dWFsX2JpbiA8LSBtb2RlbC5tYXRyaXgofiBhY3R1YWwgLSAxKQ0KICAgIGNvbG5hbWVzKGFjdHVhbF9iaW4pIDwtIGdzdWIoImFjdHVhbCIsICIiLCBjb2xuYW1lcyhhY3R1YWxfYmluKSkNCiAgICANCiAgICAjIFN0ZXAgNTogQ29tcHV0ZSBST0MgZGF0YSBmb3IgZWFjaCBjbGFzcw0KICAgIHJvY19kYXRhIDwtIGxhcHBseShsZXZlbHMoYWN0dWFsKSwgZnVuY3Rpb24oY2xhc3MpIHsNCiAgICAgIHJvY19vYmogPC0gcm9jKGFjdHVhbF9iaW5bLCBjbGFzc10sIHByb2JfZGZbW2NsYXNzXV0pDQogICAgICBhdWNfdmFsIDwtIHJvdW5kKGF1Yyhyb2Nfb2JqKSwgMykNCiAgICAgIGRhdGEuZnJhbWUoDQogICAgICAgIEZQUiA9IHJldihyb2Nfb2JqJHNwZWNpZmljaXRpZXMpLA0KICAgICAgICBUUFIgPSByZXYocm9jX29iaiRzZW5zaXRpdml0aWVzKSwNCiAgICAgICAgQ2xhc3MgPSBwYXN0ZTAoY2xhc3MsICIgKEFVQyA9ICIsIGF1Y192YWwsICIpIikNCiAgICAgICkNCiAgICB9KSAlPiUgYmluZF9yb3dzKCkNCiAgfSkNCiAgDQogICMgUmV0dXJuIGJvdGggUk9DIGRhdGEgYW5kIGV4ZWN1dGlvbiB0aW1lDQogIHJldHVybihsaXN0KHJvYyA9IHJvY19kYXRhLCB0aW1lID0gZXhlY3V0aW9uX3RpbWUpKQ0KfQ0KYGBgDQoNCiMjIEZ1bmN0aW9uIHRvIGNvbXB1dGUgUk9DIChsZXZlbCAyKQ0KDQpgYGB7cn0NCmNvbXB1dGVfcm9jX2xldmVsMiA8LSBmdW5jdGlvbihtb2RlbCwgdGVzdF9kYXRhKSB7DQogICMgTWVhc3VyZSBleGVjdXRpb24gdGltZQ0KICBleGVjdXRpb25fdGltZSA8LSBzeXN0ZW0udGltZSh7DQogICAgIyBTdGVwIDE6IFByZWRpY3QgcHJvYmFiaWxpdGllcw0KICAgIHByb2JhYmlsaXRpZXMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJwcm9iIikNCiAgICANCiAgICAjIFN0ZXAgMjogQ29udmVydCBsaXN0IG9mIG1hdHJpY2VzIHRvIGEgcHJvcGVyIGRhdGEgZnJhbWUNCiAgICBwcm9iX21hdHJpeCA8LSB0KHNhcHBseShwcm9iYWJpbGl0aWVzLCBhcy52ZWN0b3IpKQ0KICAgIHByb2JfZGYgPC0gYXMuZGF0YS5mcmFtZShwcm9iX21hdHJpeCkNCiAgICBjb2xuYW1lcyhwcm9iX2RmKSA8LSBjKCJRMSIsICJRMiIsICJRNCIsICJRNSIsICJSMSIsICJSMiIsICJSMyIsICJSNCIsICJSNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiUjYiLCAiUzMiLCAiUzQiLCAiVDEiLCAiVDMiKQ0KICAgIA0KICAgICMgU3RlcCAzOiBQcmVwYXJlIGFjdHVhbCBjbGFzcyBsYWJlbHMNCiAgICBhY3R1YWwgPC0gZmFjdG9yKHRlc3RfZGF0YSRFVU5JU2FfMiwgDQogICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJRMSIsICJRMiIsICJRNCIsICJRNSIsICJSMSIsICJSMiIsICJSMyIsICJSNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSNSIsICJSNiIsICJTMyIsICJTNCIsICJUMSIsICJUMyIpKQ0KICAgIA0KICAgICMgU3RlcCA0OiBCaW5hcml6ZSBhY3R1YWwgbGFiZWxzDQogICAgYWN0dWFsX2JpbiA8LSBtb2RlbC5tYXRyaXgofiBhY3R1YWwgLSAxKQ0KICAgIGNvbG5hbWVzKGFjdHVhbF9iaW4pIDwtIGdzdWIoImFjdHVhbCIsICIiLCBjb2xuYW1lcyhhY3R1YWxfYmluKSkNCiAgICANCiAgICAjIFN0ZXAgNTogQ29tcHV0ZSBST0MgZGF0YSBmb3IgZWFjaCBjbGFzcw0KICAgIHJvY19kYXRhIDwtIGxhcHBseShsZXZlbHMoYWN0dWFsKSwgZnVuY3Rpb24oY2xhc3MpIHsNCiAgICAgIHJvY19vYmogPC0gcm9jKGFjdHVhbF9iaW5bLCBjbGFzc10sIHByb2JfZGZbW2NsYXNzXV0pDQogICAgICBhdWNfdmFsIDwtIHJvdW5kKGF1Yyhyb2Nfb2JqKSwgMykNCiAgICAgIGRhdGEuZnJhbWUoDQogICAgICAgIEZQUiA9IHJldihyb2Nfb2JqJHNwZWNpZmljaXRpZXMpLA0KICAgICAgICBUUFIgPSByZXYocm9jX29iaiRzZW5zaXRpdml0aWVzKSwNCiAgICAgICAgQ2xhc3MgPSBwYXN0ZTAoY2xhc3MsICIgKEFVQyA9ICIsIGF1Y192YWwsICIpIikNCiAgICAgICkNCiAgICB9KSAlPiUgYmluZF9yb3dzKCkNCiAgfSkNCiAgDQogICMgUmV0dXJuIGJvdGggUk9DIGRhdGEgYW5kIGV4ZWN1dGlvbiB0aW1lDQogIHJldHVybihsaXN0KHJvYyA9IHJvY19kYXRhLCB0aW1lID0gZXhlY3V0aW9uX3RpbWUpKQ0KfQ0KYGBgDQoNCiMgRGVmaW5lIGxpc3RzIG9mIHByZWRpY3RvciB2YXJzDQoNCiMjIE1vbWVudHMgd2l0aCBzbG9wZSBtZXRob2QNCg0KYGBge3J9DQp2YXJzX1JGX3Nsb3BlIDwtIGMoDQogICMgTWluIHZhbHVlcyBvZiBhbGwgaW5kaWNlcw0KICAiTkRWSV9taW4iLCAiRVZJX21pbiIsICJORE1JX21pbiIsICJORFdJX21pbiIsICJTQVZJX21pbiIsDQogICMgTWF4IHZhbHVlcyBvZiBORE1JIGFuZCBORFdJDQogICJORE1JX21heCIsICJORFdJX21heCIsDQogICMgQVVDIG9mIE5EVkksIEVWSSBhbmQgU0FWSQ0KICAiTkRWSV9hdWNfc2xvcGUiLCAiRVZJX2F1Y19zbG9wZSIsICJTQVZJX2F1Y19zbG9wZSIsDQogICMgVmFsdWVzIG9mIE5EVkksIEVWSSBhbmQgU0FWSSBhdCBzb3MsIHBvcyBhbmQgZW9zDQogICJORFZJX3Nvc19zbG9wZV92YWx1ZSIsICJORFZJX3Bvc192YWx1ZSIsICJORFZJX2Vvc19zbG9wZV92YWx1ZSIsDQogICJFVklfc29zX3Nsb3BlX3ZhbHVlIiwgIkVWSV9wb3NfdmFsdWUiLCAiRVZJX2Vvc19zbG9wZV92YWx1ZSIsIA0KICAiU0FWSV9zb3Nfc2xvcGVfdmFsdWUiLCAiU0FWSV9wb3NfdmFsdWUiLCAiU0FWSV9lb3Nfc2xvcGVfdmFsdWUiLA0KICAjIERpZmZlcmVuY2VzIHBvcy1zb3MgaW4gdmFsdWUgYW5kIGRveQ0KICAiTkRWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfdmFsdWUiLCAiRVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIsDQogICJTQVZJX2RpZmZfcG9zX3Nvc19zbG9wZV92YWx1ZSIsIk5EVklfZGlmZl9wb3Nfc29zX3Nsb3BlX2RveSIsIA0KICAiRVZJX2RpZmZfcG9zX3Nvc19zbG9wZV9kb3kiLCAiU0FWSV9kaWZmX3Bvc19zb3Nfc2xvcGVfZG95IiwNCiAgIyBEaWZmZXJlbmNlcyBwb3MtZW9zIGluIHZhbHVlIGFuZCBkb3kNCiAgIk5EVklfZGlmZl9wb3NfZW9zX3Nsb3BlX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiLA0KICAiU0FWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfdmFsdWUiLCAiTkRWSV9kaWZmX3Bvc19lb3Nfc2xvcGVfZG95IiwgDQogICJFVklfZGlmZl9wb3NfZW9zX3Nsb3BlX2RveSIsICJTQVZJX2RpZmZfcG9zX2Vvc19zbG9wZV9kb3kiLA0KICAjIEdyb3dpbmcgc2Vhc29uIGR1cmF0aW9uDQogICJORFZJX3Nsb3BlX2dzZCIsICJFVklfc2xvcGVfZ3NkIiwgIlNBVklfc2xvcGVfZ3NkIiwNCiAgIyBDYW5vcHkgaGVpZ2h0DQogICJjYW5vcHlfaGVpZ2h0IikNCmBgYA0KDQojIyBNb21lbnRzIHdpdGggdGhyZXNob2xkIG1ldGhvZA0KDQpgYGB7cn0NCnZhcnNfUkZfdGhyZXNob2xkIDwtIGMoDQogICMgTWluIHZhbHVlcyBvZiBhbGwgaW5kaWNlcw0KICAiTkRWSV9taW4iLCAiRVZJX21pbiIsICJORE1JX21pbiIsICJORFdJX21pbiIsICJTQVZJX21pbiIsDQogICMgTWF4IHZhbHVlcyBvZiBORE1JIGFuZCBORFdJDQogICJORE1JX21heCIsICJORFdJX21heCIsDQogICMgQVVDIG9mIE5EVkksIEVWSSBhbmQgU0FWSQ0KICAiTkRWSV9hdWNfdGhyZXNob2xkIiwgIkVWSV9hdWNfdGhyZXNob2xkIiwgIlNBVklfYXVjX3RocmVzaG9sZCIsDQogICMgVmFsdWVzIG9mIE5EVkksIEVWSSBhbmQgU0FWSSBhdCBzb3MsIHBvcyBhbmQgZW9zDQogICJORFZJX3Nvc190aHJlc2hvbGRfdmFsdWUiLCAiTkRWSV9wb3NfdmFsdWUiLCAiTkRWSV9lb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgIkVWSV9zb3NfdGhyZXNob2xkX3ZhbHVlIiwgIkVWSV9wb3NfdmFsdWUiLCAiRVZJX2Vvc190aHJlc2hvbGRfdmFsdWUiLCANCiAgIlNBVklfc29zX3RocmVzaG9sZF92YWx1ZSIsICJTQVZJX3Bvc192YWx1ZSIsICJTQVZJX2Vvc190aHJlc2hvbGRfdmFsdWUiLA0KICAjIERpZmZlcmVuY2VzIHBvcy1zb3MgaW4gdmFsdWUgYW5kIGRveQ0KICAiTkRWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwgIkVWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX3ZhbHVlIiwNCiAgIlNBVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF92YWx1ZSIsIk5EVklfZGlmZl9wb3Nfc29zX3RocmVzaG9sZF9kb3kiLCANCiAgIkVWSV9kaWZmX3Bvc19zb3NfdGhyZXNob2xkX2RveSIsICJTQVZJX2RpZmZfcG9zX3Nvc190aHJlc2hvbGRfZG95IiwNCiAgIyBEaWZmZXJlbmNlcyBwb3MtZW9zIGluIHZhbHVlIGFuZCBkb3kNCiAgIk5EVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsICJFVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF92YWx1ZSIsDQogICJTQVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfdmFsdWUiLCAiTkRWSV9kaWZmX3Bvc19lb3NfdGhyZXNob2xkX2RveSIsIA0KICAiRVZJX2RpZmZfcG9zX2Vvc190aHJlc2hvbGRfZG95IiwgIlNBVklfZGlmZl9wb3NfZW9zX3RocmVzaG9sZF9kb3kiLA0KICAjIEdyb3dpbmcgc2Vhc29uIGR1cmF0aW9uDQogICJORFZJX3RocmVzaG9sZF9nc2QiLCAiRVZJX3RocmVzaG9sZF9nc2QiLCAiU0FWSV90aHJlc2hvbGRfZ3NkIiwNCiAgIyBDYW5vcHkgaGVpZ2h0DQogICJjYW5vcHlfaGVpZ2h0IikNCmBgYA0KDQojIyBNb250aHMgbWV0aG9kDQoNCmBgYHtyfQ0KdmFyc19SRl9tb250aHMgPC0gYygNCiAgIyBNaW4gdmFsdWVzIG9mIGFsbCBpbmRpY2VzDQogICJORFZJX21pbiIsICJFVklfbWluIiwgIk5ETUlfbWluIiwgIk5EV0lfbWluIiwgIlNBVklfbWluIiwNCiAgIyBNYXggdmFsdWVzIG9mIE5ETUkgYW5kIE5EV0kNCiAgIk5ETUlfbWF4IiwgIk5EV0lfbWF4IiwNCiAgIyBBVUMgb2YgTkRWSSwgRVZJIGFuZCBTQVZJIGJldHdlZW4gbWFyY2ggYW5kIG9jdA0KICAiTkRWSV9hdWNfbWFyX29jdCIsICJFVklfYXVjX21hcl9vY3QiLCAiU0FWSV9hdWNfbWFyX29jdCIsDQogICMgVmFsdWVzIG9mIE5EVkksIEVWSSBhbmQgU0FWSSBhdCBtYXJjaCwgcG9zIGFuZCBvY3QNCiAgIk5EVklfYXZnX3ZhbHVlXzAzIiwgIk5EVklfcG9zX3ZhbHVlIiwgIk5EVklfYXZnX3ZhbHVlXzEwIiwNCiAgIkVWSV9hdmdfdmFsdWVfMDMiLCAiRVZJX3Bvc192YWx1ZSIsICJFVklfYXZnX3ZhbHVlXzEwIiwgDQogICJTQVZJX2F2Z192YWx1ZV8wMyIsICJTQVZJX3Bvc192YWx1ZSIsICJTQVZJX2F2Z192YWx1ZV8xMCIsDQogICMgRGlmZmVyZW5jZXMgcG9zLW1hcmNoIGluIHZhbHVlIGFuZCBkb3kNCiAgIk5EVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUiLCAiRVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlIiwNCiAgIlNBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUiLCJORFZJX2RpZmZfcG9zX21hcmNoX2RveSIsIA0KICAiRVZJX2RpZmZfcG9zX21hcmNoX2RveSIsICJTQVZJX2RpZmZfcG9zX21hcmNoX2RveSIsDQogICMgRGlmZmVyZW5jZXMgcG9zLW9jdCBpbiB2YWx1ZSBhbmQgZG95DQogICJORFZJX2RpZmZfcG9zX29jdF92YWx1ZSIsICJFVklfZGlmZl9wb3Nfb2N0X3ZhbHVlIiwNCiAgIlNBVklfZGlmZl9wb3Nfb2N0X3ZhbHVlIiwgIk5EVklfZGlmZl9wb3Nfb2N0X2RveSIsIA0KICAiRVZJX2RpZmZfcG9zX29jdF9kb3kiLCAiU0FWSV9kaWZmX3Bvc19vY3RfZG95IiwNCiAgIyBDYW5vcHkgaGVpZ2h0DQogICJjYW5vcHlfaGVpZ2h0IikNCmBgYA0KDQojIFJvdWdoIHZhbGlkYXRpb24NCg0KRGVmaW5lIGEgc2V0IG9mIHJ1bGVzIGZvciBhIGZpcnN0IHZhbGlkYXRpb24gb2YgQUxMIFJlU3VydmV5IGRhdGEgKCJFeHBlcnQtYmFzZWQiIHJ1bGVzKS4gTm90IHZlcnkgYW1iaXRpb3VzLCBvbmx5IHRha2luZyBvdXQgb2JzZXJ2YXRpb25zIHRoYXQgYXJlIGNsZWFybHkgd3Jvbmcgb24gdGhlIGJhc2lzIG9mIGluZGljYXRvciB2YWx1ZXMuDQoNCkNyZWF0ZSBjb2x1bW4gZm9yIGZpcnN0IHZhbGlkYXRpb24gYmFzZWQgb24gZGlmZmVyZW50IGluZGljYXRvcnMsIHdoZXJlICJ3cm9uZyIgaXMgbm90ZWQgd2hlbiB0aGUgdmFsaWRhdGlvbiBydWxlIGlzIG5vdCBtZXQuIA0KDQpEZWZpbmUgcnVsZXM6DQoNCmBgYHtyfQ0KZGF0YV92YWxpZGF0aW9uIDwtDQogIGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgbXV0YXRlKA0KICAgIHZhbGlkXzFfTkRXSSA9IGNhc2Vfd2hlbigNCiAgICAgICMgUG9pbnRzIHRoYXQgYXJlIGJhc2ljYWxseSB3YXRlcg0KICAgICAgTkRXSV9tYXggPiAwLjMgfiAid3JvbmciLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pLA0KICAgIHZhbGlkXzFfQ0ggPSBjYXNlX3doZW4oDQogICAgICAjIFIgJiBRIHBvaW50cyB3aXRoIGhpZ2ggQ0gNCiAgICAgIEVVTklTYV8xICVpbiUgYygiUiIsICJRIikgJiBjYW5vcHlfaGVpZ2h0ID4gMiB+ICJ3cm9uZyIsDQogICAgICAjIFQgcG9pbnRzIHdpdGggbG93IENIDQogICAgICBFVU5JU2FfMSA9PSAiVCIgJiBjYW5vcHlfaGVpZ2h0IDwgMyB+ICJ3cm9uZyIsDQogICAgICAjIFMgcG9pbnRzIHdpdGggaGlnaCBDSA0KICAgICAgRVVOSVNhXzEgPT0gIlMiICYgY2Fub3B5X2hlaWdodCA+IDMgfiAid3JvbmciLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pLA0KICAgICMgTm8gcnVsZXMgZm9yIE5EVkksIHdlIHdpbGwgdGFrZSBvdXQgb2JzZXJ2YXRpb25zIGJhc2VkIG9uIGRpc3RyaWJ1dGlvbnMNCiAgICAjIENvdW50IGhvdyBtYW55IHZhbGlkYXRpb24gcnVsZXMgYXJlIG5vdCBtZXQNCiAgICB2YWxpZF8xX2NvdW50ID0gcm93U3VtcyhhY3Jvc3MoYyh2YWxpZF8xX05EV0ksIHZhbGlkXzFfQ0gpLCB+IC4gPT0gIndyb25nIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSwNCiAgICAjIFBvaW50cyB3aGVyZSBhdCBsZWFzdCAxIHJ1bGUgbm90IG1ldA0KICAgIHZhbGlkXzEgPSBpZl9lbHNlKHZhbGlkXzFfY291bnQgPiAwLCAiQXQgbGVhc3QgMSBydWxlIGJyb2tlbiIsDQogICAgICAgICAgICAgICAgICAgICAgIk5vIHJ1bGVzIGJyb2tlbiBzbyBmYXIiKQ0KICAgICkNCmBgYA0KDQojIERhdGEgdG8gdXNlIGluIFJGIG1vZGVscw0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGEwX3Nsb3BlIDwtIGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgbXV0YXRlKEVVTklTYV8xID0gYXMuZmFjdG9yKEVVTklTYV8xKSkgJT4lDQogICMgUmVtb3ZlIGFsbCByb3dzIHdpdGggd3JvbmcgdmFsdWVzIG9mIGluZGljZXMgKG5vdCBiZXR3ZWVuIC0xIGFuZCAxKQ0KICBkcGx5cjo6ZmlsdGVyKEVWSV9tYXggPD0gMSAmIEVWSV9taW4gPj0gLTEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5EVklfbWF4IDw9IDEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5ETUlfbWF4IDw9IDEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5EV0lfbWluID49IC0xKSAlPiUNCiAgIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGluIHByZWRpY3RvcnMNCiAgZHBseXI6OmZpbHRlcihpZl9hbGwoYWxsX29mKHZhcnNfUkZfc2xvcGUpLCB+ICFpcy5uYSguKSkpICU+JQ0KICAjIFNlbGVjdCBvbmx5IHZhcmlhYmxlcyBuZWVkZWQNCiAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBFVU5JU2FfMSwgYWxsX29mKHZhcnNfUkZfc2xvcGUpLCBMY3RubXRoLCB2YWxpZF8xKSAlPiUNCiAgIyBLZWVwIG9ubHkgcm93cyB3aXRoIGRpZmZlcmVuY2VzID4gMA0KICBkcGx5cjo6ZmlsdGVyKGlmX2FsbChjb250YWlucygiZGlmZiIpLCB+IC54ID4gMCkpDQpgYGANCg0KYGBge3J9DQpmaWx0ZXJlZF9kYXRhMF90aHJlc2hvbGQgPC0gZGF0YV92YWxpZGF0aW9uICU+JQ0KICBtdXRhdGUoRVVOSVNhXzEgPSBhcy5mYWN0b3IoRVVOSVNhXzEpKSAlPiUNCiAgIyBSZW1vdmUgYWxsIHJvd3Mgd2l0aCB3cm9uZyB2YWx1ZXMgb2YgaW5kaWNlcyAobm90IGJldHdlZW4gLTEgYW5kIDEpDQogIGRwbHlyOjpmaWx0ZXIoRVZJX21heCA8PSAxICYgRVZJX21pbiA+PSAtMSkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoTkRWSV9tYXggPD0gMSkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoTkRNSV9tYXggPD0gMSkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoTkRXSV9taW4gPj0gLTEpICU+JQ0KICAjIFJlbW92ZSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMgaW4gcHJlZGljdG9ycw0KICBkcGx5cjo6ZmlsdGVyKGlmX2FsbChhbGxfb2YodmFyc19SRl90aHJlc2hvbGQpLCB+ICFpcy5uYSguKSkpICU+JQ0KICAjIFNlbGVjdCBvbmx5IHZhcmlhYmxlcyBuZWVkZWQNCiAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBFVU5JU2FfMSwgYWxsX29mKHZhcnNfUkZfdGhyZXNob2xkKSwgTGN0bm10aCwgdmFsaWRfMSkgJT4lDQogICMgS2VlcCBvbmx5IHJvd3Mgd2l0aCBkaWZmZXJlbmNlcyA+IDANCiAgZHBseXI6OmZpbHRlcihpZl9hbGwoY29udGFpbnMoImRpZmYiKSwgfiAueCA+IDApKQ0KYGBgDQoNCmBgYHtyfQ0KZmlsdGVyZWRfZGF0YTBfbW9udGhzIDwtIGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgbXV0YXRlKEVVTklTYV8xID0gYXMuZmFjdG9yKEVVTklTYV8xKSkgJT4lDQogICMgUmVtb3ZlIGFsbCByb3dzIHdpdGggd3JvbmcgdmFsdWVzIG9mIGluZGljZXMgKG5vdCBiZXR3ZWVuIC0xIGFuZCAxKQ0KICBkcGx5cjo6ZmlsdGVyKEVWSV9tYXggPD0gMSAmIEVWSV9taW4gPj0gLTEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5EVklfbWF4IDw9IDEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5ETUlfbWF4IDw9IDEpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKE5EV0lfbWluID49IC0xKSAlPiUNCiAgIyBSZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGluIHByZWRpY3RvcnMNCiAgZHBseXI6OmZpbHRlcihpZl9hbGwoYWxsX29mKHZhcnNfUkZfbW9udGhzKSwgfiAhaXMubmEoLikpKSAlPiUNCiAgIyBTZWxlY3Qgb25seSB2YXJpYWJsZXMgbmVlZGVkDQogIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgRVVOSVNhXzEsIGFsbF9vZih2YXJzX1JGX21vbnRocyksIExjdG5tdGgsIHZhbGlkXzEpICU+JQ0KICAjIEtlZXAgb25seSByb3dzIHdpdGggZGlmZmVyZW5jZXMgPiAwDQogIGRwbHlyOjpmaWx0ZXIoaWZfYWxsKGNvbnRhaW5zKCJkaWZmIiksIH4gLnggPiAwKSkgDQpgYGANCg0KIyBUQkQ6IENvcnJlbGF0aW9uDQoNCkNvcnJlbGF0aW9uIG9mIGFsbCB2YXJpYWJsZXMgdG8gYmUgaW5jbHVkZWQgaW4gUkYgbW9kZWxzOg0KDQpgYGB7cn0NCmNvcnJwbG90KGZpbHRlcmVkX2RhdGEwX3Nsb3BlICU+JSANCiAgICAgICAgICAgZHBseXI6OnNlbGVjdChhbGxfb2YodmFyc19SRl9zbG9wZSkpICU+JQ0KICAgICAgICAgICBjb3IodXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpLA0KICAgICAgICAgbWV0aG9kID0gImNvbG9yIiwgdHlwZSA9ICJ1cHBlciIsIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDQ1KQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb21wdXRlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yX21hdHJpeF9zbG9wZSA8LSBmaWx0ZXJlZF9kYXRhMF9zbG9wZSAlPiUNCiAgZHBseXI6OnNlbGVjdChhbGxfb2YodmFyc19SRl9zbG9wZSkpICU+JQ0KICBjb3IodXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpDQoNCiMgU2V0IHRoZSBkaWFnb25hbCB0byAwIHRvIGlnbm9yZSBzZWxmLWNvcnJlbGF0aW9uDQpkaWFnKGNvcl9tYXRyaXhfc2xvcGUpIDwtIDANCg0KIyBGaW5kIHZhcmlhYmxlcyB3aGVyZSBhbGwgYWJzb2x1dGUgY29ycmVsYXRpb25zIGFyZSA8IDAuNw0Ka2VlcF92YXJzIDwtIGFwcGx5KGFicyhjb3JfbWF0cml4X3Nsb3BlKSwgMSwgZnVuY3Rpb24oeCkgYWxsKHggPCAwLjcpKQ0KDQojIFN1YnNldCB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSB0byBrZWVwIG9ubHkgdGhvc2UgdmFyaWFibGVzDQpmaWx0ZXJlZF9kYXRhMF9zbG9wZV9ub2NvcnIgPC0gKGZpbHRlcmVkX2RhdGEwX3Nsb3BlICU+JQ0KICBkcGx5cjo6c2VsZWN0KGFsbF9vZih2YXJzX1JGX3Nsb3BlKSkpWywga2VlcF92YXJzXQ0KYGBgDQoNCiMgRml0IFJGIG1vZGVscyAobGV2ZWwgMSkNCg0KIyMgV2l0aG91dCByZWZpbmVtZW50DQoNCiMjIyBHZXQgZmlsdGVyZWQgZGF0YQ0KDQpgYGB7cn0NCiMgMTogU2xvcGUgbWV0aG9kLCBubyBwcmV2aW91cyB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpmaWx0ZXJlZF9kYXRhMV9zbG9wZSA8LSBmaWx0ZXJlZF9kYXRhMF9zbG9wZSAlPiUNCiAgIyBTZWxlY3Qgb25seSBHUFMgcG9pbnRzDQogIGRwbHlyOjpmaWx0ZXIoTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBHUFMiIHwNCiAgICAgICAgICAgICAgICAgIExjdG5tdGggPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIpICU+JQ0KICBzZWxlY3QoLUxjdG5tdGgsIC12YWxpZF8xKQ0KDQojIDI6IFNsb3BlIG1ldGhvZCwgbm8gcHJldmlvdXMgdmFsaWRhdGlvbiwgR1BTIGRpZmYgcG9pbnRzDQpmaWx0ZXJlZF9kYXRhMl9zbG9wZSA8LSBmaWx0ZXJlZF9kYXRhMF9zbG9wZSAlPiUNCiAgIyBTZWxlY3Qgb25seSBHUFMgZGlmZiBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiKSAlPiUNCiAgc2VsZWN0KC1MY3RubXRoLCAtdmFsaWRfMSkNCg0KIyAzOiBTbG9wZSBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBwb2ludHMNCmZpbHRlcmVkX2RhdGEzX3Nsb3BlIDwtIGZpbHRlcmVkX2RhdGEwX3Nsb3BlICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfA0KICAgICAgICAgICAgICAgICAgTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICMgRmlsdGVyIG91dCBwb2ludHMgdGhhdCBoYXZlIG5vdCBwYXNzZWQgdGhlIHJvdWdoIHZhbGlkYXRpb24NCiAgZHBseXI6OmZpbHRlcih2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgNDogU2xvcGUgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uLCBHUFMgZGlmZiBwb2ludHMNCmZpbHRlcmVkX2RhdGE0X3Nsb3BlIDwtIGZpbHRlcmVkX2RhdGEwX3Nsb3BlICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBkaWZmIHBvaW50cw0KICBkcGx5cjo6ZmlsdGVyKExjdG5tdGggPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIpICU+JQ0KICAjIEZpbHRlciBvdXQgcG9pbnRzIHRoYXQgaGF2ZSBub3QgcGFzc2VkIHRoZSByb3VnaCB2YWxpZGF0aW9uDQogIGRwbHlyOjpmaWx0ZXIodmFsaWRfMSA9PSAiTm8gcnVsZXMgYnJva2VuIHNvIGZhciIpICU+JQ0KICBzZWxlY3QoLUxjdG5tdGgsIC12YWxpZF8xKQ0KDQojIDU6IFNsb3BlIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMTAtOTB0aCwgR1BTIHBvaW50cw0KIyA2OiBTbG9wZSBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyA3OiBTbG9wZSBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDIwLTgwdGgsIEdQUyBwb2ludHMNCiMgODogU2xvcGUgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAyMC04MHRoLCBHUFMgZGlmZiBwb2ludHMNCg0KIyA5OiBUaHJlc2hvbGQgbWV0aG9kLCBubyBwcmV2aW91cyB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpmaWx0ZXJlZF9kYXRhOV90aHJlc2hvbGQgPC0gZmlsdGVyZWRfZGF0YTBfdGhyZXNob2xkICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfA0KICAgICAgICAgICAgICAgICAgTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTA6IFRocmVzaG9sZCBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KZmlsdGVyZWRfZGF0YTEwX3RocmVzaG9sZCA8LSBmaWx0ZXJlZF9kYXRhMF90aHJlc2hvbGQgJT4lDQogICMgU2VsZWN0IG9ubHkgR1BTIGRpZmYgcG9pbnRzDQogIGRwbHlyOjpmaWx0ZXIoTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTE6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBwb2ludHMNCmZpbHRlcmVkX2RhdGExMV90aHJlc2hvbGQgPC0gZmlsdGVyZWRfZGF0YTBfdGhyZXNob2xkICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfA0KICAgICAgICAgICAgICAgICAgTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICMgRmlsdGVyIG91dCBwb2ludHMgdGhhdCBoYXZlIG5vdCBwYXNzZWQgdGhlIHJvdWdoIHZhbGlkYXRpb24NCiAgZHBseXI6OmZpbHRlcih2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTI6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KZmlsdGVyZWRfZGF0YTEyX3RocmVzaG9sZCA8LSBmaWx0ZXJlZF9kYXRhMF90aHJlc2hvbGQgJT4lDQogICMgU2VsZWN0IG9ubHkgR1BTIGRpZmYgcG9pbnRzDQogIGRwbHlyOjpmaWx0ZXIoTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICMgRmlsdGVyIG91dCBwb2ludHMgdGhhdCBoYXZlIG5vdCBwYXNzZWQgdGhlIHJvdWdoIHZhbGlkYXRpb24NCiAgZHBseXI6OmZpbHRlcih2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTM6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBwb2ludHMNCiMgMTQ6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyAxNTogVGhyZXNob2xkIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIHBvaW50cw0KIyAxNjogVGhyZXNob2xkIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIGRpZmYgcG9pbnRzDQoNCiMgMTc6IE1vbnRocyBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBwb2ludHMNCmZpbHRlcmVkX2RhdGExN19tb250aHMgPC0gZmlsdGVyZWRfZGF0YTBfbW9udGhzICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfA0KICAgICAgICAgICAgICAgICAgTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTg6IE1vbnRocyBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KZmlsdGVyZWRfZGF0YTE4X21vbnRocyA8LSBmaWx0ZXJlZF9kYXRhMF9tb250aHMgJT4lDQogICMgU2VsZWN0IG9ubHkgR1BTIGRpZmYgcG9pbnRzDQogIGRwbHlyOjpmaWx0ZXIoTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMTk6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBwb2ludHMNCmZpbHRlcmVkX2RhdGExOV9tb250aHMgPC0gZmlsdGVyZWRfZGF0YTBfbW9udGhzICU+JQ0KICAjIFNlbGVjdCBvbmx5IEdQUyBwb2ludHMNCiAgZHBseXI6OmZpbHRlcihMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIEdQUyIgfA0KICAgICAgICAgICAgICAgICAgTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICMgRmlsdGVyIG91dCBwb2ludHMgdGhhdCBoYXZlIG5vdCBwYXNzZWQgdGhlIHJvdWdoIHZhbGlkYXRpb24NCiAgZHBseXI6OmZpbHRlcih2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMjA6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KZmlsdGVyZWRfZGF0YTIwX21vbnRocyA8LSBmaWx0ZXJlZF9kYXRhMF9tb250aHMgJT4lDQogICMgU2VsZWN0IG9ubHkgR1BTIGRpZmYgcG9pbnRzDQogIGRwbHlyOjpmaWx0ZXIoTGN0bm10aCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIikgJT4lDQogICMgRmlsdGVyIG91dCBwb2ludHMgdGhhdCBoYXZlIG5vdCBwYXNzZWQgdGhlIHJvdWdoIHZhbGlkYXRpb24NCiAgZHBseXI6OmZpbHRlcih2YWxpZF8xID09ICJObyBydWxlcyBicm9rZW4gc28gZmFyIikgJT4lDQogIHNlbGVjdCgtTGN0bm10aCwgLXZhbGlkXzEpDQoNCiMgMjI6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyAyMzogTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIHBvaW50cw0KIyAyNDogTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIGRpZmYgcG9pbnRzDQpgYGANCg0KIyMjIyBOIHBvaW50cyBwZXIgY2F0ZWdvcnkNCg0KYGBge3J9DQpiaW5kX3Jvd3MoDQogIGZpbHRlcmVkX2RhdGExX3Nsb3BlICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMV9zbG9wZSIpICU+JQ0KICAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgZmlsdGVyZWRfZGF0YTJfc2xvcGUgJT4lIGNvdW50KEVVTklTYV8xKSAlPiUNCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGEyX3Nsb3BlIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhM19zbG9wZSAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFVU5JU2FfMSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUgDQogICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YTNfc2xvcGUiKSAlPiUNCiAgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogIGZpbHRlcmVkX2RhdGE0X3Nsb3BlICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhNF9zbG9wZSIpICU+JQ0KICAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgIyBmaWx0ZXJlZF9kYXRhNV9zbG9wZSAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAjICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgIyAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGE1X3Nsb3BlIikgJT4lDQogICMgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogICMgaWx0ZXJlZF9kYXRhNl9zbG9wZSAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAjICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgIyAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGE2X3Nsb3BlIikgJT4lDQogICMgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogICMgZmlsdGVyZWRfZGF0YTdfc2xvcGUgJT4lIGNvdW50KEVVTklTYV8xKSAlPiUNCiAgIyAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFVU5JU2FfMSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUgDQogICMgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhN19zbG9wZSIpICU+JQ0KICAjICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICAjIGZpbHRlcmVkX2RhdGE4X3Nsb3BlICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICMgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAjICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YThfc2xvcGUiKSAlPiUNCiAgIyAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgZmlsdGVyZWRfZGF0YTlfdGhyZXNob2xkICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhOV90aHJlc2hvbGQiKSAlPiUNCiAgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogIGZpbHRlcmVkX2RhdGExMF90aHJlc2hvbGQgJT4lIGNvdW50KEVVTklTYV8xKSAlPiUNCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZCIpICU+JQ0KICAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgZmlsdGVyZWRfZGF0YTExX3RocmVzaG9sZCAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFVU5JU2FfMSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUgDQogICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YTlfdGhyZXNob2xkIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMTJfdGhyZXNob2xkICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhOV90aHJlc2hvbGQiKSAlPiUNCiAgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogICMgZmlsdGVyZWRfZGF0YTEzX3RocmVzaG9sZCAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAjICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgIyAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZCIpICU+JQ0KICAjICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICAjIGZpbHRlcmVkX2RhdGExNF90aHJlc2hvbGQgJT4lIGNvdW50KEVVTklTYV8xKSAlPiUNCiAgIyAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFVU5JU2FfMSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUgDQogICMgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhOV90aHJlc2hvbGQiKSAlPiUNCiAgIyAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgIyBmaWx0ZXJlZF9kYXRhMTVfdGhyZXNob2xkICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICMgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAjICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YTlfdGhyZXNob2xkIikgJT4lDQogICMgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogICMgZmlsdGVyZWRfZGF0YTE2X3RocmVzaG9sZCAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAjICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgIyAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZCIpICU+JQ0KICAjICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMTdfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMTdfbW9udGhzIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMThfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMThfbW9udGhzIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMTlfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMTlfbW9udGhzIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMjBfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JQ0KICAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGEyMF9tb250aHMiKSAlPiUNCiAgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCkNCiAgIyBmaWx0ZXJlZF9kYXRhMjFfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICMgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAjICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YTIxX21vbnRocyIpICU+JQ0KICAjICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICAjIGZpbHRlcmVkX2RhdGEyMl9tb250aHMgJT4lIGNvdW50KEVVTklTYV8xKSAlPiUNCiAgIyAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFVU5JU2FfMSwgdmFsdWVzX2Zyb20gPSBuKSAlPiUgDQogICMgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMjJfbW9udGhzIikgJT4lDQogICMgICBzZWxlY3QoZGF0YSwgUSwgUiwgUywgVCksDQogICMgZmlsdGVyZWRfZGF0YTIzX21vbnRocyAlPiUgY291bnQoRVVOSVNhXzEpICU+JQ0KICAjICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgIyAgIG11dGF0ZShkYXRhID0gImZpbHRlcmVkX2RhdGEyM19tb250aHMiKSAlPiUNCiAgIyAgIHNlbGVjdChkYXRhLCBRLCBSLCBTLCBUKSwNCiAgIyBmaWx0ZXJlZF9kYXRhMjRfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICMgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRVVOSVNhXzEsIHZhbHVlc19mcm9tID0gbikgJT4lIA0KICAjICAgbXV0YXRlKGRhdGEgPSAiZmlsdGVyZWRfZGF0YTI0X21vbnRocyIpICU+JQ0KICAjICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICApDQpgYGANCg0KIyMjIFNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KYGBgDQoNCmBgYHtyfQ0KdHJhaW5faW5kaWNlczEgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMV9zbG9wZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTFfc2xvcGUpKQ0KdHJhaW5faW5kaWNlczIgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMl9zbG9wZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTJfc2xvcGUpKQ0KdHJhaW5faW5kaWNlczMgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhM19zbG9wZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTNfc2xvcGUpKQ0KdHJhaW5faW5kaWNlczQgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhNF9zbG9wZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTRfc2xvcGUpKQ0KIyB0cmFpbl9pbmRpY2VzNSA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGE1X3Nsb3BlKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTVfc2xvcGUpKQ0KIyB0cmFpbl9pbmRpY2VzNiA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGE2X3Nsb3BlKSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGE2X3Nsb3BlKSkNCiMgdHJhaW5faW5kaWNlczcgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhN19zbG9wZSksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhN19zbG9wZSkpDQojIHRyYWluX2luZGljZXM4IDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YThfc2xvcGUpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhOF9zbG9wZSkpDQp0cmFpbl9pbmRpY2VzOSA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTlfdGhyZXNob2xkKSkNCnRyYWluX2luZGljZXMxMCA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGExMF90aHJlc2hvbGQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhMTBfdGhyZXNob2xkKSkNCnRyYWluX2luZGljZXMxMSA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGExMV90aHJlc2hvbGQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhMTFfdGhyZXNob2xkKSkNCnRyYWluX2luZGljZXMxMiA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGExMl90aHJlc2hvbGQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhMTJfdGhyZXNob2xkKSkNCiMgdHJhaW5faW5kaWNlczEzIDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTEzX3RocmVzaG9sZCksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTEzX3RocmVzaG9sZCkpDQojIHRyYWluX2luZGljZXMxNCA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGExNF90aHJlc2hvbGQpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGExNF90aHJlc2hvbGQpKQ0KIyB0cmFpbl9pbmRpY2VzMTUgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMTVfdGhyZXNob2xkKSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhMTVfdGhyZXNob2xkKSkNCiMgdHJhaW5faW5kaWNlczE2IDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTE2X3RocmVzaG9sZCksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTE2X3RocmVzaG9sZCkpDQp0cmFpbl9pbmRpY2VzMTcgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMTdfbW9udGhzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTE3X21vbnRocykpDQp0cmFpbl9pbmRpY2VzMTggPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMThfbW9udGhzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTE4X21vbnRocykpDQp0cmFpbl9pbmRpY2VzMTkgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMTlfbW9udGhzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTE5X21vbnRocykpDQp0cmFpbl9pbmRpY2VzMjAgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMjBfbW9udGhzKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTIwX21vbnRocykpDQojIHRyYWluX2luZGljZXMyMSA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGEyMV9tb250aHMpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGEyMV9tb250aHMpKQ0KIyB0cmFpbl9pbmRpY2VzMjIgPC0gc2FtcGxlKDE6bnJvdyhmaWx0ZXJlZF9kYXRhMjJfbW9udGhzKSwgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhMjJfbW9udGhzKSkNCiMgdHJhaW5faW5kaWNlczIzIDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTIzX21vbnRocyksIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTIzX21vbnRocykpDQojIHRyYWluX2luZGljZXMyNCA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGEyNF9tb250aHMpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGEyNF9tb250aHMpKQ0KYGBgDQoNCmBgYHtyfQ0KdHJhaW5fZGF0YTEgPC0gZmlsdGVyZWRfZGF0YTFfc2xvcGVbdHJhaW5faW5kaWNlczEsIF0NCnRyYWluX2RhdGEyIDwtIGZpbHRlcmVkX2RhdGEyX3Nsb3BlW3RyYWluX2luZGljZXMyLCBdDQp0cmFpbl9kYXRhMyA8LSBmaWx0ZXJlZF9kYXRhM19zbG9wZVt0cmFpbl9pbmRpY2VzMywgXQ0KdHJhaW5fZGF0YTQgPC0gZmlsdGVyZWRfZGF0YTRfc2xvcGVbdHJhaW5faW5kaWNlczQsIF0NCiMgdHJhaW5fZGF0YTUgPC0gZmlsdGVyZWRfZGF0YTVfc2xvcGVbdHJhaW5faW5kaWNlczUsIF0NCiMgdHJhaW5fZGF0YTYgPC0gZmlsdGVyZWRfZGF0YTZfc2xvcGVbdHJhaW5faW5kaWNlczYsIF0NCiMgdHJhaW5fZGF0YTcgPC0gZmlsdGVyZWRfZGF0YTdfc2xvcGVbdHJhaW5faW5kaWNlczcsIF0NCiMgdHJhaW5fZGF0YTggPC0gZmlsdGVyZWRfZGF0YThfc2xvcGVbdHJhaW5faW5kaWNlczgsIF0NCnRyYWluX2RhdGE5IDwtIGZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZFt0cmFpbl9pbmRpY2VzOSwgXQ0KdHJhaW5fZGF0YTEwIDwtIGZpbHRlcmVkX2RhdGExMF90aHJlc2hvbGRbdHJhaW5faW5kaWNlczEwLCBdDQp0cmFpbl9kYXRhMTEgPC0gZmlsdGVyZWRfZGF0YTExX3RocmVzaG9sZFt0cmFpbl9pbmRpY2VzMTEsIF0NCnRyYWluX2RhdGExMiA8LSBmaWx0ZXJlZF9kYXRhMTJfdGhyZXNob2xkW3RyYWluX2luZGljZXMxMiwgXQ0KIyB0cmFpbl9kYXRhMTMgPC0gZmlsdGVyZWRfZGF0YTEzX3RocmVzaG9sZFt0cmFpbl9pbmRpY2VzMTMsIF0NCiMgdHJhaW5fZGF0YTE0IDwtIGZpbHRlcmVkX2RhdGExNF90aHJlc2hvbGRbdHJhaW5faW5kaWNlczE0LCBdDQojIHRyYWluX2RhdGExNSA8LSBmaWx0ZXJlZF9kYXRhMTVfdGhyZXNob2xkW3RyYWluX2luZGljZXMxNSwgXQ0KIyB0cmFpbl9kYXRhMTYgPC0gZmlsdGVyZWRfZGF0YTE2X3RocmVzaG9sZFt0cmFpbl9pbmRpY2VzMTYsIF0NCnRyYWluX2RhdGExNyA8LSBmaWx0ZXJlZF9kYXRhMTdfbW9udGhzW3RyYWluX2luZGljZXMxNywgXQ0KdHJhaW5fZGF0YTE4IDwtIGZpbHRlcmVkX2RhdGExOF9tb250aHNbdHJhaW5faW5kaWNlczE4LCBdDQp0cmFpbl9kYXRhMTkgPC0gZmlsdGVyZWRfZGF0YTE5X21vbnRoc1t0cmFpbl9pbmRpY2VzMTksIF0NCnRyYWluX2RhdGEyMCA8LSBmaWx0ZXJlZF9kYXRhMjBfbW9udGhzW3RyYWluX2luZGljZXMyMCwgXQ0KIyB0cmFpbl9kYXRhMjEgPC0gZmlsdGVyZWRfZGF0YTIxX21vbnRoc1t0cmFpbl9pbmRpY2VzMjEsIF0NCiMgdHJhaW5fZGF0YTIyIDwtIGZpbHRlcmVkX2RhdGEyMl9tb250aHNbdHJhaW5faW5kaWNlczIyLCBdDQojIHRyYWluX2RhdGEyMyA8LSBmaWx0ZXJlZF9kYXRhMjNfbW9udGhzW3RyYWluX2luZGljZXMyMywgXQ0KIyB0cmFpbl9kYXRhMjQgPC0gZmlsdGVyZWRfZGF0YTI0X21vbnRoc1t0cmFpbl9pbmRpY2VzMjQsIF0NCmBgYA0KDQpgYGB7cn0NCnRlc3RfZGF0YTEgPC0gZmlsdGVyZWRfZGF0YTFfc2xvcGVbLXRyYWluX2luZGljZXMxLCBdDQp0ZXN0X2RhdGEyIDwtIGZpbHRlcmVkX2RhdGEyX3Nsb3BlWy10cmFpbl9pbmRpY2VzMiwgXQ0KdGVzdF9kYXRhMyA8LSBmaWx0ZXJlZF9kYXRhM19zbG9wZVstdHJhaW5faW5kaWNlczMsIF0NCnRlc3RfZGF0YTQgPC0gZmlsdGVyZWRfZGF0YTRfc2xvcGVbLXRyYWluX2luZGljZXM0LCBdDQojIHRlc3RfZGF0YTUgPC0gZmlsdGVyZWRfZGF0YTVfc2xvcGVbLXRyYWluX2luZGljZXM1LCBdDQojIHRlc3RfZGF0YTYgPC0gZmlsdGVyZWRfZGF0YTZfc2xvcGVbLXRyYWluX2luZGljZXM2LCBdDQojIHRlc3RfZGF0YTcgPC0gZmlsdGVyZWRfZGF0YTdfc2xvcGVbLXRyYWluX2luZGljZXM3LCBdDQojIHRlc3RfZGF0YTggPC0gZmlsdGVyZWRfZGF0YThfc2xvcGVbLXRyYWluX2luZGljZXM4LCBdDQp0ZXN0X2RhdGE5IDwtIGZpbHRlcmVkX2RhdGE5X3RocmVzaG9sZFstdHJhaW5faW5kaWNlczksIF0NCnRlc3RfZGF0YTEwIDwtIGZpbHRlcmVkX2RhdGExMF90aHJlc2hvbGRbLXRyYWluX2luZGljZXMxMCwgXQ0KdGVzdF9kYXRhMTEgPC0gZmlsdGVyZWRfZGF0YTExX3RocmVzaG9sZFstdHJhaW5faW5kaWNlczExLCBdDQp0ZXN0X2RhdGExMiA8LSBmaWx0ZXJlZF9kYXRhMTJfdGhyZXNob2xkWy10cmFpbl9pbmRpY2VzMTIsIF0NCiMgdGVzdF9kYXRhMTMgPC0gZmlsdGVyZWRfZGF0YTEzX3RocmVzaG9sZFstdHJhaW5faW5kaWNlczEzLCBdDQojIHRlc3RfZGF0YTE0IDwtIGZpbHRlcmVkX2RhdGExNF90aHJlc2hvbGRbLXRyYWluX2luZGljZXMxNCwgXQ0KIyB0ZXN0X2RhdGExNSA8LSBmaWx0ZXJlZF9kYXRhMTVfdGhyZXNob2xkWy10cmFpbl9pbmRpY2VzMTUsIF0NCiMgdGVzdF9kYXRhMTYgPC0gZmlsdGVyZWRfZGF0YTE2X3RocmVzaG9sZFstdHJhaW5faW5kaWNlczE2LCBdDQp0ZXN0X2RhdGExNyA8LSBmaWx0ZXJlZF9kYXRhMTdfbW9udGhzWy10cmFpbl9pbmRpY2VzMTcsIF0NCnRlc3RfZGF0YTE4IDwtIGZpbHRlcmVkX2RhdGExOF9tb250aHNbLXRyYWluX2luZGljZXMxOCwgXQ0KdGVzdF9kYXRhMTkgPC0gZmlsdGVyZWRfZGF0YTE5X21vbnRoc1stdHJhaW5faW5kaWNlczE5LCBdDQp0ZXN0X2RhdGEyMCA8LSBmaWx0ZXJlZF9kYXRhMjBfbW9udGhzWy10cmFpbl9pbmRpY2VzMjAsIF0NCiMgdGVzdF9kYXRhMjEgPC0gZmlsdGVyZWRfZGF0YTIxX21vbnRoc1stdHJhaW5faW5kaWNlczIxLCBdDQojIHRlc3RfZGF0YTIyIDwtIGZpbHRlcmVkX2RhdGEyMl9tb250aHNbLXRyYWluX2luZGljZXMyMiwgXQ0KIyB0ZXN0X2RhdGEyMyA8LSBmaWx0ZXJlZF9kYXRhMjNfbW9udGhzWy10cmFpbl9pbmRpY2VzMjMsIF0NCiMgdGVzdF9kYXRhMjQgPC0gZmlsdGVyZWRfZGF0YTI0X21vbnRoc1stdHJhaW5faW5kaWNlczI0LCBdDQpgYGANCg0KIyMjIEZpdCBtb2RlbHMNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnJmMSA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTEsICJFVU5JU2FfMSIpDQpyZjIgPC0gcnVuX3JmKHZhcnNfUkZfc2xvcGUsIHRyYWluX2RhdGEyLCAiRVVOSVNhXzEiKQ0KcmYzIDwtIHJ1bl9yZih2YXJzX1JGX3Nsb3BlLCB0cmFpbl9kYXRhMywgIkVVTklTYV8xIikNCnJmNCA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTQsICJFVU5JU2FfMSIpDQojIHJmNSA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTUsICJFVU5JU2FfMSIpDQojIHJmNiA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTYsICJFVU5JU2FfMSIpDQojIHJmNyA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTcsICJFVU5JU2FfMSIpDQojIHJmOCA8LSBydW5fcmYodmFyc19SRl9zbG9wZSwgdHJhaW5fZGF0YTgsICJFVU5JU2FfMSIpDQpyZjkgPC0gcnVuX3JmKHZhcnNfUkZfdGhyZXNob2xkLCB0cmFpbl9kYXRhOSwgIkVVTklTYV8xIikNCnJmMTAgPC0gcnVuX3JmKHZhcnNfUkZfdGhyZXNob2xkLCB0cmFpbl9kYXRhMTAsICJFVU5JU2FfMSIpDQpyZjExIDwtIHJ1bl9yZih2YXJzX1JGX3RocmVzaG9sZCwgdHJhaW5fZGF0YTExLCAiRVVOSVNhXzEiKQ0KcmYxMiA8LSBydW5fcmYodmFyc19SRl90aHJlc2hvbGQsIHRyYWluX2RhdGExMiwgIkVVTklTYV8xIikNCiMgcmYxMyA8LSBydW5fcmYodmFyc19SRl90aHJlc2hvbGQsIHRyYWluX2RhdGExMywgIkVVTklTYV8xIikNCiMgcmYxNCA8LSBydW5fcmYodmFyc19SRl90aHJlc2hvbGQsIHRyYWluX2RhdGExNCwgIkVVTklTYV8xIikNCiMgcmYxNSA8LSBydW5fcmYodmFyc19SRl90aHJlc2hvbGQsIHRyYWluX2RhdGExNSwgIkVVTklTYV8xIikNCiMgcmYxNiA8LSBydW5fcmYodmFyc19SRl90aHJlc2hvbGQsIHRyYWluX2RhdGExNiwgIkVVTklTYV8xIikNCnJmMTcgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMTcsICJFVU5JU2FfMSIpDQpyZjE4IDwtIHJ1bl9yZih2YXJzX1JGX21vbnRocywgdHJhaW5fZGF0YTE4LCAiRVVOSVNhXzEiKQ0KcmYxOSA8LSBydW5fcmYodmFyc19SRl9tb250aHMsIHRyYWluX2RhdGExOSwgIkVVTklTYV8xIikNCnJmMjAgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjAsICJFVU5JU2FfMSIpDQojIHJmMjEgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjEsICJFVU5JU2FfMSIpDQojIHJmMjIgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjIsICJFVU5JU2FfMSIpDQojIHJmMjMgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjMsICJFVU5JU2FfMSIpDQojIHJmMjQgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjQsICJFVU5JU2FfMSIpDQpgYGANCg0KYGBge3J9DQpwcmludChyZjEkdGltZSkNCnByaW50KHJmMiR0aW1lKQ0KcHJpbnQocmYzJHRpbWUpDQpwcmludChyZjQkdGltZSkNCiMgcHJpbnQocmY1JHRpbWUpDQojIHByaW50KHJmNiR0aW1lKQ0KIyBwcmludChyZjckdGltZSkNCiMgcHJpbnQocmY4JHRpbWUpDQpwcmludChyZjkkdGltZSkNCnByaW50KHJmMTAkdGltZSkNCnByaW50KHJmMTEkdGltZSkNCnByaW50KHJmMTIkdGltZSkNCiMgcHJpbnQocmYxMyR0aW1lKQ0KIyBwcmludChyZjE0JHRpbWUpDQojIHByaW50KHJmMTUkdGltZSkNCiMgcHJpbnQocmYxNiR0aW1lKQ0KcHJpbnQocmYxNyR0aW1lKQ0KcHJpbnQocmYxOCR0aW1lKQ0KcHJpbnQocmYxOSR0aW1lKQ0KcHJpbnQocmYyMCR0aW1lKQ0KIyBwcmludChyZjIxJHRpbWUpDQojIHByaW50KHJmMjIkdGltZSkNCiMgcHJpbnQocmYyMyR0aW1lKQ0KIyBwcmludChyZjI0JHRpbWUpDQpgYGANCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2F2ZShyZjEsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjFfbW9kZWxfbDFfc2xvcGVfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShyZjIsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjJfbW9kZWxfbDFfc2xvcGVfbm92YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUocmYzLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYzX21vZGVsX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocmY0LCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY0X21vZGVsX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocmY1LA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjVfbW9kZWxfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShyZjYsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmNl9tb2RlbF9sMV9zbG9wZV9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyZjcsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmN19tb2RlbF9sMV9zbG9wZV9yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHJmOCwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY4X21vZGVsX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShyZjksIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjlfbW9kZWxfbDFfdHJlc2hvbGRfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShyZjEwLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxMF9tb2RlbF9sMV90cmVzaG9sZF9ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShyZjExLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxMV9tb2RlbF9sMV90cmVzaG9sZF9yb3VnaHZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHJmMTIsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEyX21vZGVsX2wxX3RyZXNob2xkX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocmYxMywNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxM19tb2RlbF9sMV90cmVzaG9sZF9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHJmMTQsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTRfbW9kZWxfbDFfdHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocmYxNSwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxNV9tb2RlbF9sMV90cmVzaG9sZF9yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHJmMTYsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTZfbW9kZWxfbDFfdHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHJmMTcsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE3X21vZGVsX2wxX21vbnRoc19ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHJmMTgsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE4X21vZGVsX2wxX21vbnRoc19ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShyZjE5LCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxOV9tb2RlbF9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShyZjIwLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMF9tb2RlbF9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyZjIxLCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMV9tb2RlbF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShyZjIyLA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjIyX21vZGVsX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyZjIzLA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjIzX21vZGVsX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHJmMjQsIA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjI0X21vZGVsX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNfUzIuUmRhdGEiKQ0KYGBgDQoNCiMjIyBQcmVkaWN0aW9ucw0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcHJlZGljdGlvbnNfcmYxIDwtIHByZWRpY3QocmYxJG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhMSxPT0IgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0aW9uc19yZjIgPC0gcHJlZGljdChyZjIkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGEyLE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX3JmMyA8LSBwcmVkaWN0KHJmMyRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTMsT09CID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZGljdGlvbnNfcmY0IDwtIHByZWRpY3QocmY0JG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhNCxPT0IgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRpY3Rpb25zX3JmNSA8LSBwcmVkaWN0KHJmNSRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTUsT09CID0gVFJVRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRpY3Rpb25zX3JmNiA8LSBwcmVkaWN0KHJmNiRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTYsT09CID0gVFJVRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRpY3Rpb25zX3JmNyA8LSBwcmVkaWN0KHJmNyRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTcsT09CID0gVFJVRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQojIHByZWRpY3Rpb25zX3JmOCA8LSBwcmVkaWN0KHJmOCRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTgsT09CID0gVFJVRSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0aW9uc19yZjkgPC0gcHJlZGljdChyZjkkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGE5LE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX3JmMTAgPC0gcHJlZGljdChyZjEwJG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhMTAsT09CID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZGljdGlvbnNfcmYxMSA8LSBwcmVkaWN0KHJmMTEkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExMSxPT0IgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0aW9uc19yZjEyIDwtIHByZWRpY3QocmYxMiRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTEyLE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCiMgcHJlZGljdGlvbnNfcmYxMyA8LSBwcmVkaWN0KHJmMTMkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExMyxPT0IgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCiMgcHJlZGljdGlvbnNfcmYxNCA8LSBwcmVkaWN0KHJmMTQkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExNCxPT0IgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCiMgcHJlZGljdGlvbnNfcmYxNSA8LSBwcmVkaWN0KHJmMTUkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExNSxPT0IgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCiMgcHJlZGljdGlvbnNfcmYxNiA8LSBwcmVkaWN0KHJmMTYkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExNixPT0IgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX3JmMTcgPC0gcHJlZGljdChyZjE3JG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhMTcsT09CID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KcHJlZGljdGlvbnNfcmYxOCA8LSBwcmVkaWN0KHJmMTgkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGExOCxPT0IgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQpwcmVkaWN0aW9uc19yZjE5IDwtIHByZWRpY3QocmYxOSRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTE5LE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX3JmMjAgPC0gcHJlZGljdChyZjIwJG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhMjAsT09CID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkaWN0aW9uc19yZjIxIDwtIHByZWRpY3QocmYyMSRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTIxLE9PQiA9IFRSVUUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkaWN0aW9uc19yZjIyIDwtIHByZWRpY3QocmYyMiRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTIyLE9PQiA9IFRSVUUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkaWN0aW9uc19yZjIzIDwtIHByZWRpY3QocmYyMyRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTIzLE9PQiA9IFRSVUUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KIyBwcmVkaWN0aW9uc19yZjI0IDwtIHByZWRpY3QocmYyNCRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTI0LE9PQiA9IFRSVUUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzYXZlKHByZWRpY3Rpb25zX3JmMSwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMV9wcmVkX2wxX3Nsb3BlX25vdmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocHJlZGljdGlvbnNfcmYyLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyX3ByZWRfbDFfc2xvcGVfbm92YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUocHJlZGljdGlvbnNfcmYzLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYzX3ByZWRfbDFfc2xvcGVfcm91Z2h2YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShwcmVkaWN0aW9uc19yZjQsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjRfcHJlZF9sMV9zbG9wZV9yb3VnaHZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHByZWRpY3Rpb25zX3JmNSwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY1X3ByZWRfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShwcmVkaWN0aW9uc19yZjYsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmNl9wcmVkX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHByZWRpY3Rpb25zX3JmNywNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY3X3ByZWRfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShwcmVkaWN0aW9uc19yZjgsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmOF9wcmVkX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShwcmVkaWN0aW9uc19yZjksIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjlfcHJlZF9sMV90cmVzaG9sZF9ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHByZWRpY3Rpb25zX3JmMTAsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEwX3ByZWRfbDFfdHJlc2hvbGRfbm92YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUocHJlZGljdGlvbnNfcmYxMSwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTFfcHJlZF9sMV90cmVzaG9sZF9yb3VnaHZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHByZWRpY3Rpb25zX3JmMTIsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEyX3ByZWRfbDFfdHJlc2hvbGRfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShwcmVkaWN0aW9uc19yZjEzLA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEzX3ByZWRfbDFfdHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShwcmVkaWN0aW9uc19yZjE0LA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE0X3ByZWRfbDFfdHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocHJlZGljdGlvbnNfcmYxNSwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxNV9wcmVkX2wxX3RyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUocHJlZGljdGlvbnNfcmYxNiwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxNl9wcmVkX2wxX3RyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShwcmVkaWN0aW9uc19yZjE3LCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxN19wcmVkX2wxX21vbnRoc19ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHByZWRpY3Rpb25zX3JmMTgsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE4X3ByZWRfbDFfbW9udGhzX25vdmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHByZWRpY3Rpb25zX3JmMTksIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE5X3ByZWRfbDFfbW9udGhzX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocHJlZGljdGlvbnNfcmYyMCwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjBfcHJlZF9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShwcmVkaWN0aW9uc19yZjIxLCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMV9wcmVkX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHByZWRpY3Rpb25zX3JmMjIsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjJfcHJlZF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocHJlZGljdGlvbnNfcmYyMywNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyM19wcmVkX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHByZWRpY3Rpb25zX3JmMjQsIA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjI0X3ByZWRfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQpgYGANCg0KIyMjIENvbmZ1c2lvbiBtYXRyaWNlcw0KDQpgYGB7cn0NCiMgMTogU2xvcGUgbWV0aG9kLCBubyBwcmV2aW91cyB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYxLCB0ZXN0X2RhdGExJEVVTklTYV8xKQ0KIyAyOiBTbG9wZSBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMiwgdGVzdF9kYXRhMiRFVU5JU2FfMSkNCiMgMzogU2xvcGUgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYzLCB0ZXN0X2RhdGEzJEVVTklTYV8xKQ0KIyA0OiBTbG9wZSBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmNCwgdGVzdF9kYXRhNCRFVU5JU2FfMSkNCiMgNTogU2xvcGUgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAxMC05MHRoLCBHUFMgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjUsIHRlc3RfZGF0YTUkRVVOSVNhXzEpDQojIDY6IFNsb3BlIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMTAtOTB0aCwgR1BTIGRpZmYgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjYsIHRlc3RfZGF0YTYkRVVOSVNhXzEpDQojIDc6IFNsb3BlIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIHBvaW50cw0KIyBjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmY3LCB0ZXN0X2RhdGE3JEVVTklTYV8xKQ0KIyA4OiBTbG9wZSBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDIwLTgwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyBjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmY4LCB0ZXN0X2RhdGE4JEVVTklTYV8xKQ0KIyA5OiBUaHJlc2hvbGQgbWV0aG9kLCBubyBwcmV2aW91cyB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmY5LCB0ZXN0X2RhdGE5JEVVTklTYV8xKQ0KIyAxMDogVGhyZXNob2xkIG1ldGhvZCwgbm8gcHJldmlvdXMgdmFsaWRhdGlvbiwgR1BTIGRpZmYgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYxMCwgdGVzdF9kYXRhMTAkRVVOSVNhXzEpDQojIDExOiBUaHJlc2hvbGQgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uLCBHUFMgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYxMSwgdGVzdF9kYXRhMTEkRVVOSVNhXzEpDQojIDEyOiBUaHJlc2hvbGQgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uLCBHUFMgZGlmZiBwb2ludHMNCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjEyLCB0ZXN0X2RhdGExMiRFVU5JU2FfMSkNCiMgMTM6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBwb2ludHMNCiMgY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMTMsIHRlc3RfZGF0YTEzJEVVTklTYV8xKQ0KIyAxNDogVGhyZXNob2xkIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMTAtOTB0aCwgR1BTIGRpZmYgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjE0LCB0ZXN0X2RhdGExNCRFVU5JU2FfMSkNCiMgMTU6IFRocmVzaG9sZCBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDIwLTgwdGgsIEdQUyBwb2ludHMNCiMgY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMTUsIHRlc3RfZGF0YTE1JEVVTklTYV8xKQ0KIyAxNjogVGhyZXNob2xkIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMjAtODB0aCwgR1BTIGRpZmYgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjE2LCB0ZXN0X2RhdGExNiRFVU5JU2FfMSkNCiMgMTc6IE1vbnRocyBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBwb2ludHMNCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjE3LCB0ZXN0X2RhdGExNyRFVU5JU2FfMSkNCiMgMTg6IE1vbnRocyBtZXRob2QsIG5vIHByZXZpb3VzIHZhbGlkYXRpb24sIEdQUyBkaWZmIHBvaW50cw0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMTgsIHRlc3RfZGF0YTE4JEVVTklTYV8xKQ0KIyAxOTogTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiwgR1BTIHBvaW50cw0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMTksIHRlc3RfZGF0YTE5JEVVTklTYV8xKQ0KIyAyMDogTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiwgR1BTIGRpZmYgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYyMCwgdGVzdF9kYXRhMjAkRVVOSVNhXzEpDQojIDIxOiBNb250aHMgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAxMC05MHRoLCBHUFMgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjIxLCB0ZXN0X2RhdGEyMSRFVU5JU2FfMSkNCiMgMjI6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDEwLTkwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyBjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYyMiwgdGVzdF9kYXRhMjIkRVVOSVNhXzEpDQojIDIzOiBNb250aHMgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAyMC04MHRoLCBHUFMgcG9pbnRzDQojIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjIzLCB0ZXN0X2RhdGEyMyRFVU5JU2FfMSkNCiMgMjQ6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDIwLTgwdGgsIEdQUyBkaWZmIHBvaW50cw0KIyBjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYyNCwgdGVzdF9kYXRhMjQkRVVOSVNhXzEpDQpgYGANCg0KIyMjIyBQbG90cw0KDQpgYGB7cn0NCmNtX3JmMTk8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjE5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3RfZGF0YTE5JEVVTklTYV8xKSkpDQpjb2xuYW1lcyhjbV9yZjE5KSA8LSBjKCJQcmVkaWN0aW9uIiwgIlJlZmVyZW5jZSIsICJGcmVxIikNCmBgYA0KDQpgYGB7cn0NCnBsb3RfY21fcmYxOSA8LSBnZ3Bsb3QoY21fcmYxOSwgDQogICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUmVmZXJlbmNlLCB5ID0gUHJlZGljdGlvbiwgZmlsbCA9IEZyZXEpKSArDQogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJzdGVlbGJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiQ29uZnVzaW9uIE1hdHJpeCIsIHggPSAiUmVmZXJlbmNlIiwgeSA9ICJQcmVkaWN0aW9uIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpKQ0KcGxvdF9jbV9yZjE5DQpnZ3NhdmUoaGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCAiUkYiLCAicGxvdF9jbV9yZjE5LmpwZWciKSwgcGxvdF9jbV9yZjE5LA0KICAgICAgIGRwaSA9IDMwMCwgd2lkdGggPSAzLCBoZWlnaHQgPSAzKQ0KYGBgDQoNCiMjIyBWYXJpYWJsZSBpbXBvcnRhbmNlDQoNCiMjIyMgVW5jb25kaXRpb25hbA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KdmFyaW1wX3JmMSA8LSBjb21wdXRlX3ZhcmltcChyZjEkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmMiA8LSBjb21wdXRlX3ZhcmltcChyZjIkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmMyA8LSBjb21wdXRlX3ZhcmltcChyZjMkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmNCA8LSBjb21wdXRlX3ZhcmltcChyZjQkbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXBfcmY1IDwtIGNvbXB1dGVfdmFyaW1wKHJmNSRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcF9yZjYgPC0gY29tcHV0ZV92YXJpbXAocmY2JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wX3JmNyA8LSBjb21wdXRlX3ZhcmltcChyZjckbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXBfcmY4IDwtIGNvbXB1dGVfdmFyaW1wKHJmOCRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXBfcmY5IDwtIGNvbXB1dGVfdmFyaW1wKHJmOSRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXBfcmYxMCA8LSBjb21wdXRlX3ZhcmltcChyZjEwJG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcF9yZjExIDwtIGNvbXB1dGVfdmFyaW1wKHJmMTEkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmMTIgPC0gY29tcHV0ZV92YXJpbXAocmYxMiRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcF9yZjEzIDwtIGNvbXB1dGVfdmFyaW1wKHJmMTMkbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXBfcmYxNCA8LSBjb21wdXRlX3ZhcmltcChyZjE0JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wX3JmMTUgPC0gY29tcHV0ZV92YXJpbXAocmYxNSRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcF9yZjE2IDwtIGNvbXB1dGVfdmFyaW1wKHJmMTYkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmMTcgPC0gY29tcHV0ZV92YXJpbXAocmYxNyRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXBfcmYxOCA8LSBjb21wdXRlX3ZhcmltcChyZjE4JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcF9yZjE5IDwtIGNvbXB1dGVfdmFyaW1wKHJmMTkkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wX3JmMjAgPC0gY29tcHV0ZV92YXJpbXAocmYyMCRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcF9yZjIxIDwtIGNvbXB1dGVfdmFyaW1wKHJmMjEkbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXBfcmYyMiA8LSBjb21wdXRlX3ZhcmltcChyZjIyJG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wX3JmMjMgPC0gY29tcHV0ZV92YXJpbXAocmYyMyRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcF9yZjI0IDwtIGNvbXB1dGVfdmFyaW1wKHJmMjQkbW9kZWwsIG5wZXJtID0gMTAwKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpwcmludCh2YXJpbXBfcmYxJHRpbWUpDQpwcmludCh2YXJpbXBfcmYyJHRpbWUpDQpwcmludCh2YXJpbXBfcmYzJHRpbWUpDQpwcmludCh2YXJpbXBfcmY0JHRpbWUpDQojIHByaW50KHZhcmltcF9yZjUkdGltZSkNCiMgcHJpbnQodmFyaW1wX3JmNiR0aW1lKQ0KIyBwcmludCh2YXJpbXBfcmY3JHRpbWUpDQojIHByaW50KHZhcmltcF9yZjgkdGltZSkNCnByaW50KHZhcmltcF9yZjkkdGltZSkNCnByaW50KHZhcmltcF9yZjEwJHRpbWUpDQpwcmludCh2YXJpbXBfcmYxMSR0aW1lKQ0KcHJpbnQodmFyaW1wX3JmMTIkdGltZSkNCiMgcHJpbnQodmFyaW1wX3JmMTMkdGltZSkNCiMgcHJpbnQodmFyaW1wX3JmMTQkdGltZSkNCiMgcHJpbnQodmFyaW1wX3JmMTUkdGltZSkNCiMgcHJpbnQodmFyaW1wX3JmMTYkdGltZSkNCnByaW50KHZhcmltcF9yZjE3JHRpbWUpDQpwcmludCh2YXJpbXBfcmYxOCR0aW1lKQ0KcHJpbnQodmFyaW1wX3JmMTkkdGltZSkNCnByaW50KHZhcmltcF9yZjIwJHRpbWUpDQojIHByaW50KHZhcmltcF9yZjIxJHRpbWUpDQojIHByaW50KHZhcmltcF9yZjIyJHRpbWUpDQojIHByaW50KHZhcmltcF9yZjIzJHRpbWUpDQojIHByaW50KHZhcmltcF9yZjI0JHRpbWUpDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnNhdmUodmFyaW1wX3JmMSwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMV92YXJpbXBfbDFfc2xvcGVfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXBfcmYyLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyX3ZhcmltcF9sMV9zbG9wZV9ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXBfcmYzLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYzX3ZhcmltcF9sMV9zbG9wZV9yb3VnaHZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHZhcmltcF9yZjQsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjRfdmFyaW1wX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wX3JmNSwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmNV92YXJpbXBfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZSh2YXJpbXBfcmY2LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY2X3ZhcmltcF9sMV9zbG9wZV9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZSh2YXJpbXBfcmY3LA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjdfdmFyaW1wX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wX3JmOCwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmOF92YXJpbXBfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHZhcmltcF9yZjksIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjlfdmFyaW1wX2wxX3RocmVzaG9sZF9ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHZhcmltcF9yZjEwLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxMF92YXJpbXBfbDFfdGhyZXNob2xkX25vdmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHZhcmltcF9yZjExLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxMV92YXJpbXBfbDFfdGhyZXNob2xkX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUodmFyaW1wX3JmMTIsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEyX3ZhcmltcF9sMV90aHJlc2hvbGRfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZSh2YXJpbXBfcmYxMywgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTNfdmFyaW1wX2wxX3RocmVzaG9sZF9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcF9yZjE0LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxNF92YXJpbXBfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcF9yZjE1LA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE1X3ZhcmltcF9sMV90aHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZSh2YXJpbXBfcmYxNiwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTZfdmFyaW1wX2wxX3RocmVzaG9sZF9yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUodmFyaW1wX3JmMTcsIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE3X3ZhcmltcF9sMV9tb250aHNfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXBfcmYxOCwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMThfdmFyaW1wX2wxX21vbnRoc19ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXBfcmYxOSwgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTlfdmFyaW1wX2wxX21vbnRoc19yb3VnaHZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHZhcmltcF9yZjIwLCBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMF92YXJpbXBfbDFfbW9udGhzX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wX3JmMjEsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjFfdmFyaW1wX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcF9yZjIyLCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMl92YXJpbXBfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcF9yZjIzLCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyM192YXJpbXBfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wX3JmMjQsIA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjI0X3ZhcmltcF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTZGlmZl9TMi5SZGF0YSIpDQpgYGANCg0KIyMjIyMgUGxvdHMNCg0KYGBge3J9DQpwbG90KHZhcmltcF9yZjEkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmMiR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KcGxvdCh2YXJpbXBfcmYzJHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQpwbG90KHZhcmltcF9yZjQkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCiMgcGxvdCh2YXJpbXBfcmY1JHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQojIHBsb3QodmFyaW1wX3JmNiR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KIyBwbG90KHZhcmltcF9yZjckdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCiMgcGxvdCh2YXJpbXBfcmY4JHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQpwbG90KHZhcmltcF9yZjkkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmMTAkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmMTEkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmMTIkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCiMgcGxvdCh2YXJpbXBfcmYxMyR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KIyBwbG90KHZhcmltcF9yZjE0JHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQojIHBsb3QodmFyaW1wX3JmMTUkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCiMgcGxvdCh2YXJpbXBfcmYxNiR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KcGxvdCh2YXJpbXBfcmYxNyR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KcGxvdCh2YXJpbXBfcmYxOCR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KcGxvdCh2YXJpbXBfcmYxOSR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KcGxvdCh2YXJpbXBfcmYyMCR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KIyBwbG90KHZhcmltcF9yZjIxJHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQojIHBsb3QodmFyaW1wX3JmMjIkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCiMgcGxvdCh2YXJpbXBfcmYyMyR2YXJpbXAsIG1hcmdpbiA9IGMoMTAsIDYsIDIsIDIpKQ0KIyBwbG90KHZhcmltcF9yZjI0JHZhcmltcCwgbWFyZ2luID0gYygxMCwgNiwgMiwgMikpDQpgYGANCg0KIyMjIyBDb25kaXRpb25hbCAoVEJEKQ0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KdmFyaW1wLWNvbmRfcmYxIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYxJG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMiA8LSBjb21wdXRlX3ZhcmltcF9jb25kKHJmMiRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXAtY29uZF9yZjMgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjMkbW9kZWwsIG5wZXJtID0gMTAwKQ0KdmFyaW1wLWNvbmRfcmY0IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY0JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmY1IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY1JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmY2IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY2JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmY3IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY3JG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmY4IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY4JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmOSA8LSBjb21wdXRlX3ZhcmltcF9jb25kKHJmOSRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXAtY29uZF9yZjEwIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYxMCRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXAtY29uZF9yZjExIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYxMSRtb2RlbCwgbnBlcm0gPSAxMDApDQp2YXJpbXAtY29uZF9yZjEyIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYxMiRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcC1jb25kX3JmMTMgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjEzJG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmYxNCA8LSBjb21wdXRlX3ZhcmltcF9jb25kKHJmMTQkbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXAtY29uZF9yZjE1IDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYxNSRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcC1jb25kX3JmMTYgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjE2JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMTcgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjE3JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMTggPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjE4JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMTkgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjE5JG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMjAgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjIwJG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcC1jb25kX3JmMjEgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjIxJG1vZGVsLCBucGVybSA9IDEwMCkNCiMgdmFyaW1wLWNvbmRfcmYyMiA8LSBjb21wdXRlX3ZhcmltcF9jb25kKHJmMjIkbW9kZWwsIG5wZXJtID0gMTAwKQ0KIyB2YXJpbXAtY29uZF9yZjIzIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmYyMyRtb2RlbCwgbnBlcm0gPSAxMDApDQojIHZhcmltcC1jb25kX3JmMjQgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjI0JG1vZGVsLCBucGVybSA9IDEwMCkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcHJpbnQodmFyaW1wLWNvbmRfcmYxJHRpbWUpDQpwcmludCh2YXJpbXAtY29uZF9yZjIkdGltZSkNCnByaW50KHZhcmltcC1jb25kX3JmMyR0aW1lKQ0KcHJpbnQodmFyaW1wLWNvbmRfcmY0JHRpbWUpDQojIHByaW50KHZhcmltcC1jb25kX3JmNSR0aW1lKQ0KIyBwcmludCh2YXJpbXAtY29uZF9yZjYkdGltZSkNCiMgcHJpbnQodmFyaW1wLWNvbmRfcmY3JHRpbWUpDQojIHByaW50KHZhcmltcC1jb25kX3JmOCR0aW1lKQ0KcHJpbnQodmFyaW1wLWNvbmRfcmY5JHRpbWUpDQpwcmludCh2YXJpbXAtY29uZF9yZjEwJHRpbWUpDQpwcmludCh2YXJpbXAtY29uZF9yZjExJHRpbWUpDQpwcmludCh2YXJpbXAtY29uZF9yZjEyJHRpbWUpDQojIHByaW50KHZhcmltcC1jb25kX3JmMTMkdGltZSkNCiMgcHJpbnQodmFyaW1wLWNvbmRfcmYxNCR0aW1lKQ0KIyBwcmludCh2YXJpbXAtY29uZF9yZjE1JHRpbWUpDQojIHByaW50KHZhcmltcC1jb25kX3JmMTYkdGltZSkNCnByaW50KHZhcmltcC1jb25kX3JmMTckdGltZSkNCnByaW50KHZhcmltcC1jb25kX3JmMTgkdGltZSkNCnByaW50KHZhcmltcC1jb25kX3JmMTkkdGltZSkNCnByaW50KHZhcmltcC1jb25kX3JmMjAkdGltZSkNCiMgcHJpbnQodmFyaW1wLWNvbmRfcmYyMSR0aW1lKQ0KIyBwcmludCh2YXJpbXAtY29uZF9yZjIyJHRpbWUpDQojIHByaW50KHZhcmltcC1jb25kX3JmMjMkdGltZSkNCiMgcHJpbnQodmFyaW1wLWNvbmRfcmYyNCR0aW1lKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzYXZlKHZhcmltcC1jb25kX3JmMSwgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjFfdmFyaW1wLWNvbmRfbDFfc2xvcGVfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjIsDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjJfdmFyaW1wLWNvbmRfbDFfc2xvcGVfbm92YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUodmFyaW1wLWNvbmRfcmYzLA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYzX3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUodmFyaW1wLWNvbmRfcmY0LA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY0X3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmY1LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY1X3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmY2LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY2X3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcC1jb25kX3JmNywNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY3X3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmY4LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY4X3ZhcmltcC1jb25kX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjksIA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY5X3ZhcmltcC1jb25kX2wxX3RocmVzaG9sZF9ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHZhcmltcC1jb25kX3JmMTAsDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEwX3ZhcmltcC1jb25kX2wxX3RocmVzaG9sZF9ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjExLA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxMV92YXJpbXAtY29uZF9sMV90aHJlc2hvbGRfcm91Z2h2YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjEyLCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTJfdmFyaW1wLWNvbmRfbDFfdGhyZXNob2xkX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYxMywgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTNfdmFyaW1wLWNvbmRfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYxNCwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTRfdmFyaW1wLWNvbmRfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHZhcmltcC1jb25kX3JmMTUsDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTVfdmFyaW1wLWNvbmRfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYxNiwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTZfdmFyaW1wLWNvbmRfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjE3LA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxN192YXJpbXAtY29uZF9sMV9tb250aHNfbm92YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjE4LCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMThfdmFyaW1wLWNvbmRfbDFfbW9udGhzX25vdmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHZhcmltcC1jb25kX3JmMTksIA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxOV92YXJpbXAtY29uZF9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXAtY29uZF9yZjIwLCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjBfdmFyaW1wLWNvbmRfbDFfbW9udGhzX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYyMSwNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMV92YXJpbXAtY29uZF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZSh2YXJpbXAtY29uZF9yZjIyLCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMl92YXJpbXAtY29uZF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYyMywgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjNfdmFyaW1wLWNvbmRfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUodmFyaW1wLWNvbmRfcmYyNCwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjRfdmFyaW1wLWNvbmRfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KYGBgDQoNCiMjIyMjIFBsb3RzDQoNCmBgYHtyfQ0KcGxvdCh2YXJpbXAtY29uZF9yZjEkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KcGxvdCh2YXJpbXAtY29uZF9yZjIkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KcGxvdCh2YXJpbXAtY29uZF9yZjMkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KcGxvdCh2YXJpbXAtY29uZF9yZjQkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmNSR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQojIHBsb3QodmFyaW1wLWNvbmRfcmY2JHZhcmltcCwgbWFyZ2luID0gYyg5LCAzLCAyLCAyKSkNCiMgcGxvdCh2YXJpbXAtY29uZF9yZjckdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmOCR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpwbG90KHZhcmltcC1jb25kX3JmOSR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpwbG90KHZhcmltcC1jb25kX3JmMTAkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KcGxvdCh2YXJpbXAtY29uZF9yZjExJHZhcmltcCwgbWFyZ2luID0gYyg5LCAzLCAyLCAyKSkNCnBsb3QodmFyaW1wLWNvbmRfcmYxMiR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQojIHBsb3QodmFyaW1wLWNvbmRfcmYxMyR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQojIHBsb3QodmFyaW1wLWNvbmRfcmYxNCR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQojIHBsb3QodmFyaW1wLWNvbmRfcmYxNSR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQojIHBsb3QodmFyaW1wLWNvbmRfcmYxNiR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpwbG90KHZhcmltcC1jb25kX3JmMTckdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KcGxvdCh2YXJpbXAtY29uZF9yZjE4JHZhcmltcCwgbWFyZ2luID0gYyg5LCAzLCAyLCAyKSkNCnBsb3QodmFyaW1wLWNvbmRfcmYxOSR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpwbG90KHZhcmltcC1jb25kX3JmMjAkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmMjEkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmMjIkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmMjMkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KIyBwbG90KHZhcmltcC1jb25kX3JmMjQkdmFyaW1wLCBtYXJnaW4gPSBjKDksIDMsIDIsIDIpKQ0KYGBgDQoNCiMjIyBST0MgY3VydmVzIChUQkQpDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpyb2NfZGF0YTEgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmMSRtb2RlbCwgdGVzdF9kYXRhMSkNCnJvY19kYXRhMiA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYyJG1vZGVsLCB0ZXN0X2RhdGEyKQ0Kcm9jX2RhdGEzIDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjMkbW9kZWwsIHRlc3RfZGF0YTMpDQpyb2NfZGF0YTQgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmNCRtb2RlbCwgdGVzdF9kYXRhNCkNCiMgcm9jX2RhdGE1IDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjUkbW9kZWwsIHRlc3RfZGF0YTUpDQojIHJvY19kYXRhNiA8LSBjb21wdXRlX3JvY19sZXZlbDEocmY2JG1vZGVsLCB0ZXN0X2RhdGE2KQ0KIyByb2NfZGF0YTcgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmNyRtb2RlbCwgdGVzdF9kYXRhNykNCiMgcm9jX2RhdGE4IDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjgkbW9kZWwsIHRlc3RfZGF0YTgpDQpyb2NfZGF0YTkgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmOSRtb2RlbCwgdGVzdF9kYXRhOSkNCnJvY19kYXRhMTAgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmMTAkbW9kZWwsIHRlc3RfZGF0YTEwKQ0Kcm9jX2RhdGExMSA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYxMSRtb2RlbCwgdGVzdF9kYXRhMTEpDQpyb2NfZGF0YTEyIDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjEyJG1vZGVsLCB0ZXN0X2RhdGExMikNCiMgcm9jX2RhdGExMyA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYxMyRtb2RlbCwgdGVzdF9kYXRhMTMpDQojIHJvY19kYXRhMTQgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmMTQkbW9kZWwsIHRlc3RfZGF0YTE0KQ0KIyByb2NfZGF0YTE1IDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjE1JG1vZGVsLCB0ZXN0X2RhdGExNSkNCiMgcm9jX2RhdGExNiA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYxNiRtb2RlbCwgdGVzdF9kYXRhMTYpDQpyb2NfZGF0YTE3IDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjE3JG1vZGVsLCB0ZXN0X2RhdGExNykNCnJvY19kYXRhMTggPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmMTgkbW9kZWwsIHRlc3RfZGF0YTE4KQ0Kcm9jX2RhdGExOSA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYxOSRtb2RlbCwgdGVzdF9kYXRhMTkpDQpyb2NfZGF0YTIwIDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjIwJG1vZGVsLCB0ZXN0X2RhdGEyMCkNCiMgcm9jX2RhdGEyMSA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYyMSRtb2RlbCwgdGVzdF9kYXRhMjEpDQojIHJvY19kYXRhMjIgPC0gY29tcHV0ZV9yb2NfbGV2ZWwxKHJmMjIkbW9kZWwsIHRlc3RfZGF0YTIyKQ0KIyByb2NfZGF0YTIzIDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjIzJG1vZGVsLCB0ZXN0X2RhdGEyMykNCiMgcm9jX2RhdGEyNCA8LSBjb21wdXRlX3JvY19sZXZlbDEocmYyNCRtb2RlbCwgdGVzdF9kYXRhMjQpDQpgYGANCg0KYGBge3J9DQpwcmludChyb2NfZGF0YTEkdGltZSkNCnByaW50KHJvY19kYXRhMiR0aW1lKQ0KcHJpbnQocm9jX2RhdGEzJHRpbWUpDQpwcmludChyb2NfZGF0YTQkdGltZSkNCiMgcHJpbnQocm9jX2RhdGE1JHRpbWUpDQojIHByaW50KHJvY19kYXRhNiR0aW1lKQ0KIyBwcmludChyb2NfZGF0YTckdGltZSkNCiMgcHJpbnQocm9jX2RhdGE4JHRpbWUpDQpwcmludChyb2NfZGF0YTkkdGltZSkNCnByaW50KHJvY19kYXRhMTAkdGltZSkNCnByaW50KHJvY19kYXRhMTEkdGltZSkNCnByaW50KHJvY19kYXRhMTIkdGltZSkNCiMgcHJpbnQocm9jX2RhdGExMyR0aW1lKQ0KIyBwcmludChyb2NfZGF0YTE0JHRpbWUpDQojIHByaW50KHJvY19kYXRhMTUkdGltZSkNCiMgcHJpbnQocm9jX2RhdGExNiR0aW1lKQ0KcHJpbnQocm9jX2RhdGExNyR0aW1lKQ0KcHJpbnQocm9jX2RhdGExOCR0aW1lKQ0KcHJpbnQocm9jX2RhdGExOSR0aW1lKQ0KcHJpbnQocm9jX2RhdGEyMCR0aW1lKQ0KIyBwcmludChyb2NfZGF0YTIxJHRpbWUpDQojIHByaW50KHJvY19kYXRhMjIkdGltZSkNCiMgcHJpbnQocm9jX2RhdGEyMyR0aW1lKQ0KIyBwcmludChyb2NfZGF0YTI0JHRpbWUpDQpgYGANCg0KYGBge3J9DQpzYXZlKHJvY19kYXRhMSwgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjFfcm9jZGF0YV9sMV9zbG9wZV9ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHJvY19kYXRhMiwNCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMl9yb2NkYXRhX2wxX3Nsb3BlX25vdmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHJvY19kYXRhMywNCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmM19yb2NkYXRhX2wxX3Nsb3BlX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocm9jX2RhdGE0LA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY0X3JvY2RhdGFfbDFfc2xvcGVfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTUsIA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjVfcm9jZGF0YV9sMV9zbG9wZV9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0KIyBzYXZlKHJvY19kYXRhNiwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmNl9yb2NkYXRhX2wxX3Nsb3BlX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU2RpZmZfUzIuUmRhdGEiKQ0KIyBzYXZlKHJvY19kYXRhNywNCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY3X3JvY2RhdGFfbDFfc2xvcGVfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTgsIA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjhfcm9jZGF0YV9sMV9zbG9wZV9yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUocm9jX2RhdGE5LA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmY5X3JvY2RhdGFfbDFfdGhyZXNob2xkX25vdmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocm9jX2RhdGExMCwNCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTBfcm9jZGF0YV9sMV90aHJlc2hvbGRfbm92YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCnNhdmUocm9jX2RhdGExMSwgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjExX3JvY2RhdGFfbDFfdGhyZXNob2xkX3JvdWdodmFsaWRfR1BTX1MyLlJkYXRhIikNCnNhdmUocm9jX2RhdGExMiwgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjEyX3JvY2RhdGFfbDFfdGhyZXNob2xkX3JvdWdodmFsaWRfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocm9jX2RhdGExMywgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTNfcm9jZGF0YV9sMV90aHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTE0LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxNF9yb2NkYXRhX2wxX3RocmVzaG9sZF9yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTE1LA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjE1X3JvY2RhdGFfbDFfdGhyZXNob2xkX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUocm9jX2RhdGExNiwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTZfcm9jZGF0YV9sMV90aHJlc2hvbGRfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTZGlmZl9TMi5SZGF0YSIpDQpzYXZlKHJvY19kYXRhMTcsIA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxN19yb2NkYXRhX2wxX21vbnRoc19ub3ZhbGlkX0dQU19TMi5SZGF0YSIpDQpzYXZlKHJvY19kYXRhMTgsIA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYxOF9yb2NkYXRhX2wxX21vbnRoc19ub3ZhbGlkX0dQU2RpZmZfUzIuUmRhdGEiKQ0Kc2F2ZShyb2NfZGF0YTE5LCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMTlfcm9jZGF0YV9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShyb2NfZGF0YTIwLCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjBfcm9jZGF0YV9sMV9tb250aHNfcm91Z2h2YWxpZF9HUFNkaWZmX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTIxLA0KIyAgICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjIxX3JvY2RhdGFfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4xMDkwX0dQU19TMi5SZGF0YSIpDQojIHNhdmUocm9jX2RhdGEyMiwgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjJfcm9jZGF0YV9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjEwOTBfR1BTZGlmZl9TMi5SZGF0YSIpDQojIHNhdmUocm9jX2RhdGEyMywgDQojICAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjNfcm9jZGF0YV9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCiMgc2F2ZShyb2NfZGF0YTI0LCANCiMgICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyNF9yb2NkYXRhX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMjA4MF9HUFNkaWZmX1MyLlJkYXRhIikNCmBgYA0KDQojIyMjIFBsb3RzDQoNCmBgYHtyfQ0Kcm9jMSA8LSBnZ3Bsb3Qocm9jX2RhdGExJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCnJvYzIgPC0gZ2dwbG90KHJvY19kYXRhMiRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2MzIDwtIGdncGxvdChyb2NfZGF0YTMkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiAgbGFicyh0aXRsZSA9ICJNdWx0aWNsYXNzIFJPQyBDdXJ2ZXMgd2l0aCBBVUMiLCB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLA0KICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwgY29sb3IgPSAiQ2xhc3MgKEFVQykiKSArDQogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0Kcm9jNCA8LSBnZ3Bsb3Qocm9jX2RhdGE0JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiMgcm9jNSA8LSBnZ3Bsb3Qocm9jX2RhdGE1JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQojICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiMgICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQojICAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KIyAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KIyByb2M2IDwtIGdncGxvdChyb2NfZGF0YTYkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiMgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KIyAgIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiMgICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwgY29sb3IgPSAiQ2xhc3MgKEFVQykiKSArDQojICAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQojIHJvYzcgPC0gZ2dwbG90KHJvY19kYXRhNyRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KIyAgIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQojICAgbGFicyh0aXRsZSA9ICJNdWx0aWNsYXNzIFJPQyBDdXJ2ZXMgd2l0aCBBVUMiLCB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLA0KIyAgICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiMgICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiMgcm9jOCA8LSBnZ3Bsb3Qocm9jX2RhdGE4JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQojICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiMgICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQojICAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KIyAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0Kcm9jOSA8LSBnZ3Bsb3Qocm9jX2RhdGE5JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCnJvYzEwIDwtIGdncGxvdChyb2NfZGF0YTEwJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCnJvYzExIDwtIGdncGxvdChyb2NfZGF0YTExJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCnJvYzEyIDwtIGdncGxvdChyb2NfZGF0YTEyJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiMgcm9jMTMgPC0gZ2dwbG90KHJvY19kYXRhMTMkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiMgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KIyAgIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiMgICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwgY29sb3IgPSAiQ2xhc3MgKEFVQykiKSArDQojICAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQojIHJvYzE0IDwtIGdncGxvdChyb2NfZGF0YTE0JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQojICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiMgICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQojICAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KIyAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KIyByb2MxNSA8LSBnZ3Bsb3Qocm9jX2RhdGExNSRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KIyAgIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQojICAgbGFicyh0aXRsZSA9ICJNdWx0aWNsYXNzIFJPQyBDdXJ2ZXMgd2l0aCBBVUMiLCB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLA0KIyAgICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiMgICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiMgcm9jMTYgPC0gZ2dwbG90KHJvY19kYXRhMTYkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiMgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KIyAgIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiMgICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwgY29sb3IgPSAiQ2xhc3MgKEFVQykiKSArDQojICAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2MxNyA8LSBnZ3Bsb3Qocm9jX2RhdGExNyRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2MxOCA8LSBnZ3Bsb3Qocm9jX2RhdGExOCRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2MxOSA8LSBnZ3Bsb3Qocm9jX2RhdGExOSRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2MyMCA8LSBnZ3Bsb3Qocm9jX2RhdGEyMCRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQojIHJvYzIxIDwtIGdncGxvdChyb2NfZGF0YTIxJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQojICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiMgICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQojICAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KIyAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KIyByb2MyMiA8LSBnZ3Bsb3Qocm9jX2RhdGEyMiRyb2MsIGFlcyh4ID0gRlBSLCB5ID0gVFBSLCBjb2xvciA9IENsYXNzKSkgKw0KIyAgIGdlb21fbGluZShzaXplID0gMS4yKSArIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQojICAgbGFicyh0aXRsZSA9ICJNdWx0aWNsYXNzIFJPQyBDdXJ2ZXMgd2l0aCBBVUMiLCB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLA0KIyAgICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLCBjb2xvciA9ICJDbGFzcyAoQVVDKSIpICsNCiMgICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiMgcm9jMjMgPC0gZ2dwbG90KHJvY19kYXRhMjMkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiMgICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKyBnZW9tX2FibGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmF5IikgKw0KIyAgIGxhYnModGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwNCiMgICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwgY29sb3IgPSAiQ2xhc3MgKEFVQykiKSArDQojICAgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQojIHJvYzI0IDwtIGdncGxvdChyb2NfZGF0YTI0JHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQojICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiMgICBsYWJzKHRpdGxlID0gIk11bHRpY2xhc3MgUk9DIEN1cnZlcyB3aXRoIEFVQyIsIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQojICAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIsIGNvbG9yID0gIkNsYXNzIChBVUMpIikgKw0KIyAgIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCmBgYHtyfQ0Kcm9jMQ0Kcm9jMg0Kcm9jMw0Kcm9jNA0KIyByb2M1DQojIHJvYzYNCiMgcm9jNw0KIyByb2M4DQpyb2M5DQpyb2MxMA0Kcm9jMTENCnJvYzEyDQojIHJvYzEzDQojIHJvYzE0DQojIHJvYzE1DQojIHJvYzE2DQpyb2MxNw0Kcm9jMTgNCnJvYzE5DQpyb2MyMA0KIyByb2MyMQ0KIyByb2MyMg0KIyByb2MyMw0KIyByb2MyNA0KYGBgDQoNCiMjIFdpdGggcmVmaW5lbWVudA0KDQojIyMgUmVmaW5lbWVudA0KDQpCYXNlZCBvbiB0aGUgNCBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMgKGV4Y2VwdCBjYW5vcHlfaGVpZ2h0KS4gSWYgdGhlIHNhbWUgdHlwZSBvZiB2YXJpYWJsZSBpcyB3aXRoaW4gdGhlIDQgbW9zdCBpbXBvcnRhbnQgZm9yIGRpZmZlcmVudCBpbmRpY2VzLCB0YWtlIG5leHQgdmFyaWFibGUgb2YgZGlmZmVyZW50IHR5cGUuDQpGb3IgcmYxOSAoTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiwgR1BTIHBvaW50cyksIHZhcmlhYmxlcyBmb3IgcmVmaW5lbWVudCBhcmU6IEVWSV9wb3NfdmFsdWUsIFNBVklfYXVjX21hcl9vY3QsIFNBVklfYXZnX3ZhbHVlXzAzLCBORFZJX21pbi4gDQoNCmBgYHtyfQ0KZGlzdHJfcGxvdF9wZXJjZW50aWxlcyA8LSBmdW5jdGlvbihkYXRhLCB5X3ZhcnMsIHlfbGFiZWxzKSB7DQogIGZvciAoaSBpbiBzZXFfYWxvbmcoeV92YXJzKSkgew0KICAgIHlfdmFyIDwtIHlfdmFyc1tbaV1dDQogICAgeV9sYWJlbCA8LSB5X2xhYmVsc1tbaV1dDQogICAgDQogICAgIyBDYWxjdWxhdGUgcGVyY2VudGlsZXMgcGVyIEVVTklTYV8xIGdyb3VwDQogICAgcGVyY2VudGlsZXMgPC0gZGF0YSAlPiUNCiAgICAgIGdyb3VwX2J5KEVVTklTYV8xKSAlPiUNCiAgICAgIHN1bW1hcmlzZSgNCiAgICAgICAgcDEwID0gcXVhbnRpbGUoLmRhdGFbW3lfdmFyXV0sIDAuMSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgcDkwID0gcXVhbnRpbGUoLmRhdGFbW3lfdmFyXV0sIDAuOSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICAgKQ0KICAgIA0KICAgICMgSm9pbiBwZXJjZW50aWxlcyBiYWNrIHRvIGRhdGENCiAgICBkYXRhX2ZsYWdnZWQgPC0gZGF0YSAlPiUNCiAgICAgIGxlZnRfam9pbihwZXJjZW50aWxlcywgYnkgPSAiRVVOSVNhXzEiKSAlPiUNCiAgICAgIG11dGF0ZShvdXRsaWVyX2ZsYWcgPSBjYXNlX3doZW4oDQogICAgICAgIC5kYXRhW1t5X3Zhcl1dIDwgcDEwIH4gImxvdyIsDQogICAgICAgIC5kYXRhW1t5X3Zhcl1dID4gcDkwIH4gImhpZ2giLA0KICAgICAgICBUUlVFIH4gIm1pZCINCiAgICAgICkpDQogICAgDQogICAgIyBGaWx0ZXIgYW5kIHBsb3QNCiAgICBwIDwtIGdncGxvdChkYXRhID0gZGF0YV9mbGFnZ2VkICU+JQ0KICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSksDQogICAgICAgICAgICAgICAgYWVzKHggPSBFVU5JU2FfMV9kZXNjciwgeSA9IC5kYXRhW1t5X3Zhcl1dKSkgKw0KICAgICAgZ2VvbV9mbGF0X3Zpb2xpbihhZXMoZmlsbCA9IEVVTklTYV8xX2Rlc2NyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4yLCB5ID0gMCksIGFscGhhID0gMC44KSArDQogICAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGlmZWxzZShvdXRsaWVyX2ZsYWcgPT0gIm1pZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMV9kZXNjciwgImdyZXkiKSksDQogICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSksIHNpemUgPSAxLA0KICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNikgKw0KICAgICAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsID0gRVVOSVNhXzFfZGVzY3IpLCB3aWR0aCA9IDAuMiwgb3V0bGllci5zaGFwZSA9IE5BLA0KICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41KSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gMjAsIHNpemUgPSAxKSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBmdW5jdGlvbih4KSBkYXRhLmZyYW1lKHkgPSBtYXgoeCwgbmEucm0gPSBUUlVFKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4xLCBsYWJlbCA9IGxlbmd0aCh4KSksDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwgYWVzKGxhYmVsID0gLi5sYWJlbC4uKSwgdmp1c3QgPSAwLjUpICsNCiAgICAgIGxhYnMoeSA9IHlfbGFiZWwsIHggPSAiRVVOSVMgbGV2ZWwgMSIpICsNCiAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxNSkpICsNCiAgICAgIGd1aWRlcyhmaWxsID0gRkFMU0UsIGNvbG9yID0gRkFMU0UpICsNCiAgICAgIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkgKw0KICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoDQogICAgICAgICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIgPSAiI0Y4NzY2RCIsDQogICAgICAgICJHcmFzc2xhbmRzIiA9ICIjN0NBRTAwIiwNCiAgICAgICAgIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiID0gIiMwMEJGQzQiLA0KICAgICAgICAiV2V0bGFuZHMiID0gIiNDNzdDRkYiLA0KICAgICAgICAiZ3JleSIgPSAiZ3JleSIpKQ0KICAgIA0KICAgIHByaW50KHApDQogIH0NCn0NCmBgYA0KDQpgYGB7cn0NCmRpc3RyX3Bsb3RfcGVyY2VudGlsZXMoDQogICMgR1BTIHBvaW50cyBhZnRlciByb3VnaCB2YWxpZGF0aW9uDQogIGZpbHRlcmVkX2RhdGExOV9tb250aHMgJT4lDQogICAgIyBKb2luIHRvIGdldCBFVU5JUyAxIGRlc2NyaXB0aW9ucw0KICAgIGxlZnRfam9pbihkYXRhX3ZhbGlkYXRpb24gJT4lDQogICAgICAgICAgICAgICAgc2VsZWN0KFBsb3RPYnNlcnZhdGlvbklELCBFVU5JU2FfMV9kZXNjcikpLA0KICBjKCJFVklfcG9zX3ZhbHVlIiwgIlNBVklfYXVjX21hcl9vY3QiLCAiU0FWSV9hdmdfdmFsdWVfMDMiLCAiTkRWSV9taW4iKSwNCiAgYygiRVZJIG1heCB2YWx1ZSIsICJTQVZJIEFVQyIsICJBVmVyYWdlIFNBVkkgdmFsdWUgZm9yIE1hcmNoIiwgIk5EVkkgbWluIHZhbHVlIikpDQpgYGANCg0KQ2FsY3VsYXRlIHBlcmNlbnRpbGVzOg0KDQpgYGB7cn0NCnBlcmNlbnRpbGVzX21vbnRocyA8LSANCiAgIyBHUFMgcG9pbnRzIGFmdGVyIHJvdWdoIHZhbGlkYXRpb24NCiAgZmlsdGVyZWRfZGF0YTE5X21vbnRocyAlPiUNCiAgZ3JvdXBfYnkoRVVOSVNhXzEpICU+JQ0KICBzdW1tYXJpemUoDQogICAgcGVyY18xMF9TQVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlID0gcXVhbnRpbGUoU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC4xMCwgbmEucm0gPSBUKSwNCiAgICBwZXJjXzIwX1NBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUgPSBxdWFudGlsZShTQVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfODBfU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSA9IHF1YW50aWxlKFNBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuODAsIG5hLnJtID0gVCksDQogICAgcGVyY185MF9TQVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlID0gcXVhbnRpbGUoU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfMTBfRVZJX2F1Y19tYXJfb2N0ID0gcXVhbnRpbGUoRVZJX2F1Y19tYXJfb2N0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfMjBfRVZJX2F1Y19tYXJfb2N0ID0gcXVhbnRpbGUoRVZJX2F1Y19tYXJfb2N0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfODBfRVZJX2F1Y19tYXJfb2N0ID0gcXVhbnRpbGUoRVZJX2F1Y19tYXJfb2N0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjgwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfOTBfRVZJX2F1Y19tYXJfb2N0ID0gcXVhbnRpbGUoRVZJX2F1Y19tYXJfb2N0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuOTAsIG5hLnJtID0gVCksDQogICAgcGVyY18xMF9TQVZJX2RpZmZfcG9zX29jdF9kb3kgPSBxdWFudGlsZShTQVZJX2RpZmZfcG9zX29jdF9kb3ksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVCksDQogICAgcGVyY18yMF9TQVZJX2RpZmZfcG9zX29jdF9kb3kgPSBxdWFudGlsZShTQVZJX2RpZmZfcG9zX29jdF9kb3ksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMjAsIG5hLnJtID0gVCksDQogICAgcGVyY184MF9TQVZJX2RpZmZfcG9zX29jdF9kb3kgPSBxdWFudGlsZShTQVZJX2RpZmZfcG9zX29jdF9kb3ksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuODAsIG5hLnJtID0gVCksDQogICAgcGVyY185MF9TQVZJX2RpZmZfcG9zX29jdF9kb3kgPSBxdWFudGlsZShTQVZJX2RpZmZfcG9zX29jdF9kb3ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjXzEwX0VWSV9wb3NfdmFsdWUgPSBxdWFudGlsZShFVklfcG9zX3ZhbHVlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNfMjBfRVZJX3Bvc192YWx1ZSA9IHF1YW50aWxlKEVWSV9wb3NfdmFsdWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMjAsIG5hLnJtID0gVCksDQogICAgcGVyY184MF9FVklfcG9zX3ZhbHVlID0gcXVhbnRpbGUoRVZJX3Bvc192YWx1ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC44MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjXzkwX0VWSV9wb3NfdmFsdWUgPSBxdWFudGlsZShFVklfcG9zX3ZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuOTAsIG5hLnJtID0gVCkNCiAgICAgICAgICAgICkNCmBgYA0KDQojIyMgR2V0IGZpbHRlcmVkIGRhdGENCg0KYGBge3J9DQojIDIxOiBNb250aHMgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAxMC05MHRoLCBHUFMgcG9pbnRzDQpmaWx0ZXJlZF9kYXRhMjFfbW9udGhzIDwtIA0KICAjIEdQUyBwb2ludHMgYWZ0ZXIgcm91Z2ggdmFsaWRhdGlvbg0KICBmaWx0ZXJlZF9kYXRhMTlfbW9udGhzICU+JQ0KICBsZWZ0X2pvaW4ocGVyY2VudGlsZXNfbW9udGhzLCBieSA9ICJFVU5JU2FfMSIpICU+JQ0KICBtdXRhdGUoRVVOSVNhXzEgPSBhcy5mYWN0b3IoRVVOSVNhXzEpKSAlPiUNCiAgZHBseXI6OmZpbHRlcigNCiAgICAoU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSA+PSBwZXJjXzEwX1NBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUgJg0KICAgICAgIFNBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUgPD0gcGVyY185MF9TQVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlKSAmDQogICAgICAoRVZJX2F1Y19tYXJfb2N0ID49IHBlcmNfMTBfRVZJX2F1Y19tYXJfb2N0ICYNCiAgICAgICAgIEVWSV9hdWNfbWFyX29jdCA8PSBwZXJjXzkwX0VWSV9hdWNfbWFyX29jdCkgJg0KICAgICAgKFNBVklfZGlmZl9wb3Nfb2N0X2RveSA+PSBwZXJjXzEwX1NBVklfZGlmZl9wb3Nfb2N0X2RveSAmDQogICAgICAgICBTQVZJX2RpZmZfcG9zX29jdF9kb3kgPD0gcGVyY185MF9TQVZJX2RpZmZfcG9zX29jdF9kb3kpICYNCiAgICAgIChFVklfcG9zX3ZhbHVlID49IHBlcmNfMTBfRVZJX3Bvc192YWx1ZSAmDQogICAgICAgICBFVklfcG9zX3ZhbHVlIDw9IHBlcmNfOTBfRVZJX3Bvc192YWx1ZSkNCiAgICApDQoNCiMgMjM6IE1vbnRocyBtZXRob2QsIHJvdWdoIHZhbGlkYXRpb24gKyByZWZpbmVtZW50IDIwLTgwdGgsIEdQUyBwb2ludHMNCmZpbHRlcmVkX2RhdGEyM19tb250aHMgPC0gDQogICMgR1BTIHBvaW50cyBhZnRlciByb3VnaCB2YWxpZGF0aW9uDQogIGZpbHRlcmVkX2RhdGExOV9tb250aHMgJT4lDQogIGxlZnRfam9pbihwZXJjZW50aWxlc19tb250aHMsIGJ5ID0gIkVVTklTYV8xIikgJT4lDQogIG11dGF0ZShFVU5JU2FfMSA9IGFzLmZhY3RvcihFVU5JU2FfMSkpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKA0KICAgIChTQVZJX2RpZmZfcG9zX21hcmNoX3ZhbHVlID49IHBlcmNfMjBfU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSAmDQogICAgICAgU0FWSV9kaWZmX3Bvc19tYXJjaF92YWx1ZSA8PSBwZXJjXzgwX1NBVklfZGlmZl9wb3NfbWFyY2hfdmFsdWUpICYNCiAgICAgIChFVklfYXVjX21hcl9vY3QgPj0gcGVyY18yMF9FVklfYXVjX21hcl9vY3QgJg0KICAgICAgICAgRVZJX2F1Y19tYXJfb2N0IDw9IHBlcmNfODBfRVZJX2F1Y19tYXJfb2N0KSAmDQogICAgICAoU0FWSV9kaWZmX3Bvc19vY3RfZG95ID49IHBlcmNfMjBfU0FWSV9kaWZmX3Bvc19vY3RfZG95ICYNCiAgICAgICAgIFNBVklfZGlmZl9wb3Nfb2N0X2RveSA8PSBwZXJjXzgwX1NBVklfZGlmZl9wb3Nfb2N0X2RveSkgJg0KICAgICAgKEVWSV9wb3NfdmFsdWUgPj0gcGVyY18yMF9FVklfcG9zX3ZhbHVlICYNCiAgICAgICAgIEVWSV9wb3NfdmFsdWUgPD0gcGVyY184MF9FVklfcG9zX3ZhbHVlKQ0KICAgICkNCmBgYA0KDQojIyMjIE4gcGxvdHMgcGVyIGNhdGVnb3J5DQoNCmBgYHtyfQ0KYmluZF9yb3dzKA0KICBmaWx0ZXJlZF9kYXRhMjFfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMjFfbW9udGhzIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KICBmaWx0ZXJlZF9kYXRhMjNfbW9udGhzICU+JSBjb3VudChFVU5JU2FfMSkgJT4lDQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEVVTklTYV8xLCB2YWx1ZXNfZnJvbSA9IG4pICU+JSANCiAgICBtdXRhdGUoZGF0YSA9ICJmaWx0ZXJlZF9kYXRhMjNfbW9udGhzIikgJT4lDQogICAgc2VsZWN0KGRhdGEsIFEsIFIsIFMsIFQpLA0KKQ0KYGBgDQoNCiMjIyBTcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0cw0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCmBgYA0KDQpgYGB7cn0NCnRyYWluX2luZGljZXMyMSA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGEyMV9tb250aHMpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGEyMV9tb250aHMpKQ0KdHJhaW5faW5kaWNlczIzIDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTIzX21vbnRocyksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIDAuNyAqIG5yb3coZmlsdGVyZWRfZGF0YTIzX21vbnRocykpDQpgYGANCg0KYGBge3J9DQp0cmFpbl9kYXRhMjEgPC0gZmlsdGVyZWRfZGF0YTIxX21vbnRoc1t0cmFpbl9pbmRpY2VzMjEsIF0NCnRyYWluX2RhdGEyMyA8LSBmaWx0ZXJlZF9kYXRhMjNfbW9udGhzW3RyYWluX2luZGljZXMyMywgXQ0KYGBgDQoNCmBgYHtyfQ0KdGVzdF9kYXRhMjEgPC0gZmlsdGVyZWRfZGF0YTIxX21vbnRoc1stdHJhaW5faW5kaWNlczIxLCBdDQp0ZXN0X2RhdGEyMyA8LSBmaWx0ZXJlZF9kYXRhMjNfbW9udGhzWy10cmFpbl9pbmRpY2VzMjMsIF0NCmBgYA0KDQojIyMgRml0IG1vZGVscw0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcmYyMSA8LSBydW5fcmYodmFyc19SRl9tb250aHMsIHRyYWluX2RhdGEyMSwgIkVVTklTYV8xIikNCnJmMjMgPC0gcnVuX3JmKHZhcnNfUkZfbW9udGhzLCB0cmFpbl9kYXRhMjMsICJFVU5JU2FfMSIpDQpgYGANCg0KYGBge3J9DQpwcmludChyZjIxJHRpbWUpDQpwcmludChyZjIzJHRpbWUpDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnNhdmUocmYyMSwgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjIxX21vZGVsX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShyZjIzLCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjNfbW9kZWxfbDFfbW9udGhzX3JvdWdodmFsaWQtcmVmaW4yMDgwX0dQU19TMi5SZGF0YSIpDQpgYGANCg0KIyMjIFByZWRpY3Rpb25zDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpwcmVkaWN0aW9uc19yZjIxIDwtIHByZWRpY3QocmYyMSRtb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YTIxLE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnByZWRpY3Rpb25zX3JmMjMgPC0gcHJlZGljdChyZjIzJG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhMjMsT09CID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzYXZlKHByZWRpY3Rpb25zX3JmMjEsIA0KICAgICBmaWxlID0gIm9iamVjdHMvUkYvUzIvcmYyMV9wcmVkX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZShwcmVkaWN0aW9uc19yZjIzLCANCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjNfcHJlZF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCmBgYA0KDQojIyMgQ29uZnVzaW9uIG1hdHJpY2VzDQoNCmBgYHtyfQ0KIyAyMTogTW9udGhzIG1ldGhvZCwgcm91Z2ggdmFsaWRhdGlvbiArIHJlZmluZW1lbnQgMTAtOTB0aCwgR1BTIHBvaW50cw0KY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zX3JmMjEsIHRlc3RfZGF0YTIxJEVVTklTYV8xKQ0KDQojIDIzOiBNb250aHMgbWV0aG9kLCByb3VnaCB2YWxpZGF0aW9uICsgcmVmaW5lbWVudCAyMC04MHRoLCBHUFMgcG9pbnRzDQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmYyMywgdGVzdF9kYXRhMjMkRVVOSVNhXzEpDQpgYGANCg0KIyMjIyBQbG90cw0KDQpgYGB7cn0NCmNtX3JmMjE8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjIxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3RfZGF0YTIxJEVVTklTYV8xKSkpDQpjb2xuYW1lcyhjbV9yZjIxKSA8LSBjKCJQcmVkaWN0aW9uIiwgIlJlZmVyZW5jZSIsICJGcmVxIikNCmNtX3JmMjM8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjIzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3RfZGF0YTIzJEVVTklTYV8xKSkpDQpjb2xuYW1lcyhjbV9yZjIzKSA8LSBjKCJQcmVkaWN0aW9uIiwgIlJlZmVyZW5jZSIsICJGcmVxIikNCmBgYA0KDQpgYGB7cn0NCnBsb3RfY21fcmYyMSA8LSBnZ3Bsb3QoY21fcmYyMSwgDQogICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUmVmZXJlbmNlLCB5ID0gUHJlZGljdGlvbiwgZmlsbCA9IEZyZXEpKSArDQogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJzdGVlbGJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiQ29uZnVzaW9uIE1hdHJpeCIsIHggPSAiUmVmZXJlbmNlIiwgeSA9ICJQcmVkaWN0aW9uIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpKQ0KcGxvdF9jbV9yZjIzIDwtIGdncGxvdChjbV9yZjIzLCANCiAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBSZWZlcmVuY2UsIHkgPSBQcmVkaWN0aW9uLCBmaWxsID0gRnJlcSkpICsNCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDUpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInN0ZWVsYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJDb25mdXNpb24gTWF0cml4IiwgeCA9ICJSZWZlcmVuY2UiLCB5ID0gIlByZWRpY3Rpb24iKSArDQogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIikpDQpwbG90X2NtX3JmMjENCnBsb3RfY21fcmYyMw0KZ2dzYXZlKGhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwgIlJGIiwgInBsb3RfY21fcmYyMS5qcGVnIiksIHBsb3RfY21fcmYyMSwNCiAgICAgICBkcGkgPSAzMDAsIHdpZHRoID0gMywgaGVpZ2h0ID0gMykNCmdnc2F2ZShoZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsICJSRiIsICJwbG90X2NtX3JmMjMuanBlZyIpLCBwbG90X2NtX3JmMjMsDQogICAgICAgZHBpID0gMzAwLCB3aWR0aCA9IDMsIGhlaWdodCA9IDMpDQpgYGANCg0KIyMjIFZhcmlhYmxlIGltcG9ydGFuY2UNCg0KIyMjIyBVbmNvbmRpdGlvbmFsDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQp2YXJpbXBfcmYyMSA8LSBjb21wdXRlX3ZhcmltcChyZjIxJG1vZGVsLCBucGVybSA9IDEwMCkNCnZhcmltcF9yZjIzIDwtIGNvbXB1dGVfdmFyaW1wKHJmMjMkbW9kZWwsIG5wZXJtID0gMTAwKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpwcmludCh2YXJpbXBfcmYyMSR0aW1lKQ0KcHJpbnQodmFyaW1wX3JmMjMkdGltZSkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2F2ZSh2YXJpbXBfcmYyMSwNCiAgICAgZmlsZSA9ICJvYmplY3RzL1JGL1MyL3JmMjFfdmFyaW1wX2wxX21vbnRoc19yb3VnaHZhbGlkLXJlZmluMTA5MF9HUFNfUzIuUmRhdGEiKQ0Kc2F2ZSh2YXJpbXBfcmYyMywgDQogICAgIGZpbGUgPSAib2JqZWN0cy9SRi9TMi9yZjIzX3ZhcmltcF9sMV9tb250aHNfcm91Z2h2YWxpZC1yZWZpbjIwODBfR1BTX1MyLlJkYXRhIikNCmBgYA0KDQojIyMjIyBQbG90cw0KDQpgYGB7cn0NCnBsb3QodmFyaW1wX3JmMjEkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmMjMkdmFyaW1wLCBtYXJnaW4gPSBjKDEwLCA2LCAyLCAyKSkNCmBgYA0KDQoNCg0KDQoNCg0KIyBPTEQsIHRha2UgZnJvbSBoZXJlDQoNCiMgUkYgbW1vZGVscyBhZnRlciB0cmFpbmluZyBkYXRhIHJlZmluZW1lbnQNCg0KIyMgNjogR1BTIHBvaW50cywgd2l0aGluIDEwLTkwdGggcGVyY2VudGlsZQ0KDQpSZWZpbmVtZW50IGJhc2VkIG9uIHRoZSB2YXJpYWJsZXM6IFNBVklfcG9zX3ZhbHVlLCBORFdJX21pbiwgTkRNSV9taW4sIE5ETUlfbWF4IChsYXRlciBjaGVjayB2YXJpYWJsZSBpbXBvcnRhbmNlcyB3ZWxsISkuDQoNCkRpc3RyaWJ1dGlvbiBwbG90czoNCg0KYGBge3J9DQpkaXN0cl9wbG90X3BlcmNlbnRpbGVzIDwtIGZ1bmN0aW9uKGRhdGEsIHlfdmFycywgeV9sYWJlbHMpIHsNCiAgZm9yIChpIGluIHNlcV9hbG9uZyh5X3ZhcnMpKSB7DQogICAgeV92YXIgPC0geV92YXJzW1tpXV0NCiAgICB5X2xhYmVsIDwtIHlfbGFiZWxzW1tpXV0NCiAgICANCiAgICAjIENhbGN1bGF0ZSBwZXJjZW50aWxlcyBwZXIgRVVOSVNhXzEgZ3JvdXANCiAgICBwZXJjZW50aWxlcyA8LSBkYXRhICU+JQ0KICAgICAgZ3JvdXBfYnkoRVVOSVNhXzEpICU+JQ0KICAgICAgc3VtbWFyaXNlKA0KICAgICAgICBwMTAgPSBxdWFudGlsZSguZGF0YVtbeV92YXJdXSwgMC4xLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICBwOTAgPSBxdWFudGlsZSguZGF0YVtbeV92YXJdXSwgMC45LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApDQogICAgDQogICAgIyBKb2luIHBlcmNlbnRpbGVzIGJhY2sgdG8gZGF0YQ0KICAgIGRhdGFfZmxhZ2dlZCA8LSBkYXRhICU+JQ0KICAgICAgbGVmdF9qb2luKHBlcmNlbnRpbGVzLCBieSA9ICJFVU5JU2FfMSIpICU+JQ0KICAgICAgbXV0YXRlKG91dGxpZXJfZmxhZyA9IGNhc2Vfd2hlbigNCiAgICAgICAgLmRhdGFbW3lfdmFyXV0gPCBwMTAgfiAibG93IiwNCiAgICAgICAgLmRhdGFbW3lfdmFyXV0gPiBwOTAgfiAiaGlnaCIsDQogICAgICAgIFRSVUUgfiAibWlkIg0KICAgICAgKSkNCiAgICANCiAgICAjIEZpbHRlciBhbmQgcGxvdA0KICAgIHAgPC0gZ2dwbG90KGRhdGEgPSBkYXRhX2ZsYWdnZWQgJT4lDQogICAgICAgICAgICAgICAgICBmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpLA0KICAgICAgICAgICAgICAgIGFlcyh4ID0gRVVOSVNhXzFfZGVzY3IsIHkgPSAuZGF0YVtbeV92YXJdXSkpICsNCiAgICAgIGdlb21fZmxhdF92aW9saW4oYWVzKGZpbGwgPSBFVU5JU2FfMV9kZXNjciksDQogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IDAuMiwgeSA9IDApLCBhbHBoYSA9IDAuOCkgKw0KICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBpZmVsc2Uob3V0bGllcl9mbGFnID09ICJtaWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzFfZGVzY3IsICJncmV5IikpLA0KICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMTUpLCBzaXplID0gMSwNCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjYpICsNCiAgICAgIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IEVVTklTYV8xX2Rlc2NyKSwgd2lkdGggPSAwLjIsIG91dGxpZXIuc2hhcGUgPSBOQSwNCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICAgICAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lYW4sIGdlb20gPSAicG9pbnQiLCBzaGFwZSA9IDIwLCBzaXplID0gMSkgKw0KICAgICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZnVuY3Rpb24oeCkgZGF0YS5mcmFtZSh5ID0gbWF4KHgsIG5hLnJtID0gVFJVRSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAuMSwgbGFiZWwgPSBsZW5ndGgoeCkpLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAidGV4dCIsIGFlcyhsYWJlbCA9IC4ubGFiZWwuLiksIHZqdXN0ID0gMC41KSArDQogICAgICBsYWJzKHkgPSB5X2xhYmVsLCB4ID0gIkVVTklTIGxldmVsIDEiKSArDQogICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTUpKSArDQogICAgICBndWlkZXMoZmlsbCA9IEZBTFNFLCBjb2xvciA9IEZBTFNFKSArDQogICAgICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQogICAgDQogICAgcHJpbnQocCkNCiAgfQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KZGlzdHJfcGxvdF9wZXJjZW50aWxlcyhkYXRhX3ZhbGlkYXRpb24gJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgIyBHZXQgR1BTIHBvaW50cyBhZnRlciByb3VnaCB2YWxpZGF0aW9uDQogICAgICAgICAgICAgZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEICVpbiUgZmlsdGVyZWRfZGF0YTMkUGxvdE9ic2VydmF0aW9uSUQpLA0KICAgICAgICAgICBjKCJjYW5vcHlfaGVpZ2h0IiwgIlNBVklfcG9zX3ZhbHVlIiwgIk5EV0lfbWluIiwgIk5ETUlfbWluIiwgDQogICAgICAgICAgICAgIk5ETUlfbWF4IiksDQogICAgICAgICAgIGMoImNhbm9weV9oZWlnaHQiLCAiU0FWSV9wb3NfdmFsdWUiLCAiTkRXSV9taW4iLCAiTkRNSV9taW4iLCANCiAgICAgICAgICAgICAiTkRNSV9tYXgiKSkNCmBgYA0KDQpTbyBmYXIgbm90IHVzaW5nIGNhbm9weV9oZWlnaHQgZm9yIHJlZmluZW1lbnQuDQoNCkNhbGN1bGF0ZSBwZXJjZW50aWxlczoNCg0KYGBge3J9DQpwZXJjZW50aWxlczYgPC0gZGF0YV92YWxpZGF0aW9uICU+JQ0KICAjIEdldCBHUFMgcG9pbnRzIGFmdGVyIHJvdWdoIHZhbGlkYXRpb24NCiAgZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEICVpbiUgZmlsdGVyZWRfZGF0YTMkUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICBncm91cF9ieShFVU5JU2FfMSkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBwZXJjZW50aWxlXzEwX1NBVklfcG9zX3ZhbHVlID0gcXVhbnRpbGUoU0FWSV9wb3NfdmFsdWUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVCksDQogICAgcGVyY2VudGlsZV8yMF9TQVZJX3Bvc192YWx1ZSA9IHF1YW50aWxlKFNBVklfcG9zX3ZhbHVlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNlbnRpbGVfODBfU0FWSV9wb3NfdmFsdWUgPSBxdWFudGlsZShTQVZJX3Bvc192YWx1ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC44MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzkwX1NBVklfcG9zX3ZhbHVlID0gcXVhbnRpbGUoU0FWSV9wb3NfdmFsdWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzEwX05EV0lfbWluID0gcXVhbnRpbGUoTkRXSV9taW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVCksDQogICAgcGVyY2VudGlsZV8yMF9ORFdJX21pbiA9IHF1YW50aWxlKE5EV0lfbWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNlbnRpbGVfODBfTkRXSV9taW4gPSBxdWFudGlsZShORFdJX21pbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC44MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzkwX05EV0lfbWluID0gcXVhbnRpbGUoTkRXSV9taW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzEwX05ETUlfbWluID0gcXVhbnRpbGUoTkRNSV9taW4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVCksDQogICAgcGVyY2VudGlsZV8yMF9ORE1JX21pbiA9IHF1YW50aWxlKE5ETUlfbWluLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNlbnRpbGVfODBfTkRNSV9taW4gPSBxdWFudGlsZShORE1JX21pbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC44MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzkwX05ETUlfbWluID0gcXVhbnRpbGUoTkRNSV9taW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzEwX05ETUlfbWF4ID0gcXVhbnRpbGUoTkRNSV9tYXgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVCksDQogICAgcGVyY2VudGlsZV8yMF9ORE1JX21heCA9IHF1YW50aWxlKE5ETUlfbWF4LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjIwLCBuYS5ybSA9IFQpLA0KICAgIHBlcmNlbnRpbGVfODBfTkRNSV9tYXggPSBxdWFudGlsZShORE1JX21heCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC44MCwgbmEucm0gPSBUKSwNCiAgICBwZXJjZW50aWxlXzkwX05ETUlfbWF4ID0gcXVhbnRpbGUoTkRNSV9tYXgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUKQ0KICAgICAgICAgICAgKQ0KYGBgDQoNCmBgYHtyfQ0KZmlsdGVyZWRfZGF0YTYgPC0gZGF0YV92YWxpZGF0aW9uICU+JQ0KICAjIEdldCBHUFMgcG9pbnRzIGFmdGVyIHJvdWdoIHZhbGlkYXRpb24NCiAgZmlsdGVyKFBsb3RPYnNlcnZhdGlvbklEICVpbiUgZmlsdGVyZWRfZGF0YTMkUGxvdE9ic2VydmF0aW9uSUQpICU+JQ0KICBzZWxlY3QoUGxvdE9ic2VydmF0aW9uSUQsIEVVTklTYV8xLCBhbGxfb2YodmFyc19SRikpICU+JQ0KICBsZWZ0X2pvaW4ocGVyY2VudGlsZXM2LCBieSA9ICJFVU5JU2FfMSIpICU+JQ0KICBtdXRhdGUoRVVOSVNhXzEgPSBhcy5mYWN0b3IoRVVOSVNhXzEpKSAlPiUNCiAgZmlsdGVyKA0KICAgIChTQVZJX3Bvc192YWx1ZSA+PSBwZXJjZW50aWxlXzEwX1NBVklfcG9zX3ZhbHVlICYNCiAgICAgICBTQVZJX3Bvc192YWx1ZSA8PSBwZXJjZW50aWxlXzkwX1NBVklfcG9zX3ZhbHVlKSAmDQogICAgICAoTkRXSV9taW4gPj0gcGVyY2VudGlsZV8xMF9ORFdJX21pbiAmDQogICAgICAgICBORFdJX21pbiA8PSBwZXJjZW50aWxlXzkwX05EV0lfbWluKSAmDQogICAgICAoTkRNSV9taW4gPj0gcGVyY2VudGlsZV8xMF9ORE1JX21pbiAmDQogICAgICAgICBORE1JX21pbiA8PSBwZXJjZW50aWxlXzkwX05ETUlfbWluKSAmDQogICAgICAoTkRNSV9tYXggPj0gcGVyY2VudGlsZV8xMF9ORE1JX21heCAmDQogICAgICAgICBORE1JX21heCA8PSBwZXJjZW50aWxlXzkwX05ETUlfbWF4KQ0KICAgICkNCmBgYA0KDQpTcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEgc2V0cy4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQp0cmFpbl9pbmRpY2VzNiA8LSBzYW1wbGUoMTpucm93KGZpbHRlcmVkX2RhdGE2KSwgMC43ICogbnJvdyhmaWx0ZXJlZF9kYXRhNikpDQp0cmFpbl9kYXRhNiA8LSBmaWx0ZXJlZF9kYXRhNlt0cmFpbl9pbmRpY2VzNiwgXQ0KdGVzdF9kYXRhNiA8LSBmaWx0ZXJlZF9kYXRhNlstdHJhaW5faW5kaWNlczYsIF0NCmBgYA0KDQpOdW1iZXIgb2YgcG9pbnRzIHBlciBjYXRlZ29yeSBmb3IgZmlsdGVyZWQgZGF0YToNCg0KYGBge3J9DQpmaWx0ZXJlZF9kYXRhNiAlPiUgY291bnQoRVVOSVNhXzEpDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnJmNl9TMiA8LSBydW5fcmYodmFyc19SRiwgdHJhaW5fZGF0YTYsICJFVU5JU2FfMSIpDQpwcmludChyZjZfUzIkdGltZSkNCnNhdmUocmY2X1MyLCBmaWxlID0gIm9iamVjdHMvMTAvcmY2X1MyLlJkYXRhIikNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcHJlZGljdGlvbnNfcmY2X1MyIDwtIHByZWRpY3QocmY2X1MyJG1vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhNixPT0IgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZXNwb25zZSIpDQpzYXZlKHByZWRpY3Rpb25zX3JmNl9TMiwgZmlsZSA9ICJvYmplY3RzLzEwL3ByZWRpY3Rpb25zX3JmNl9TMi5SZGF0YSIpDQpgYGANCg0KQ29uZnVzaW9uIG1hdHJpeDoNCg0KYGBge3J9DQpjb25mdXNpb25NYXRyaXgocHJlZGljdGlvbnNfcmY2X1MyLCB0ZXN0X2RhdGE2JEVVTklTYV8xKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQp2YXJpbXBfcmY2X1MyIDwtIGNvbXB1dGVfdmFyaW1wKHJmNl9TMiRtb2RlbCwgbnBlcm0gPSAxMDApDQpzYXZlKHZhcmltcF9yZjZfUzIsIGZpbGUgPSAib2JqZWN0cy8xMC92YXJpbXBfcmY2X1MyLlJkYXRhIikNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KdmFyaW1wX3JmNl9jb25kX1MyIDwtIGNvbXB1dGVfdmFyaW1wX2NvbmQocmY2X1MyJG1vZGVsLCBucGVybSA9IDEwMCkNCnByaW50KHZhcmltcF9yZjZfY29uZF9TMiR0aW1lKQ0Kc2F2ZSh2YXJpbXBfcmY2X2NvbmRfUzIsIGZpbGUgPSAib2JqZWN0cy8xMC92YXJpbXBfcmY2X2NvbmRfUzIuUmRhdGEiKQ0KYGBgDQoNClZhcmlhYmxlIEltcG9ydGFuY2UgUGxvdA0KDQpgYGB7cn0NCnBsb3QodmFyaW1wX3JmNl9TMiR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpwbG90KHZhcmltcF9yZjZfY29uZF9TMiR2YXJpbXAsIG1hcmdpbiA9IGMoOSwgMywgMiwgMikpDQpgYGANCg0KUk9DIGN1cnZlczoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnJvY19kYXRhNl9TMiA8LSBjb21wdXRlX3JvY19sZXZlbDEocmY2X1MyJG1vZGVsLCB0ZXN0X2RhdGE2KQ0Kc2F2ZShyb2NfZGF0YTZfUzIsIGZpbGUgPSAib2JqZWN0cy8xMC9yb2NfZGF0YTZfUzIuUmRhdGEiKQ0KYGBgDQoNCmBgYHtyfQ0Kcm9jNiA8LSBnZ3Bsb3Qocm9jX2RhdGE2X1MyJHJvYywgYWVzKHggPSBGUFIsIHkgPSBUUFIsIGNvbG9yID0gQ2xhc3MpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTXVsdGljbGFzcyBST0MgQ3VydmVzIHdpdGggQVVDIiwNCiAgICB4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLA0KICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIiwNCiAgICBjb2xvciA9ICJDbGFzcyAoQVVDKSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0Kcm9jNg0KYGBgDQoNCiMjIDc6IEdQUyBwb2ludHMsIHdpdGhpbiAyMC04MHRoIHBlcmNlbnRpbGUNCg0KYGBge3J9DQpmaWx0ZXJlZF9kYXRhNyA8LSBkYXRhX3ZhbGlkYXRpb24gJT4lDQogICMgR2V0IEdQUyBwb2ludHMgYWZ0ZXIgcm91Z2ggdmFsaWRhdGlvbg0KICBmaWx0ZXIoUGxvdE9ic2VydmF0aW9uSUQgJWluJSBmaWx0ZXJlZF9kYXRhMyRQbG90T2JzZXJ2YXRpb25JRCkgJT4lDQogIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgRVVOSVNhXzEsIGFsbF9vZih2YXJzX1JGKSkgJT4lDQogIGxlZnRfam9pbihwZXJjZW50aWxlczYsIGJ5ID0gIkVVTklTYV8xIikgJT4lDQogIG11dGF0ZShFVU5JU2FfMSA9IGFzLmZhY3RvcihFVU5JU2FfMSkpICU+JQ0KICBmaWx0ZXIoDQogICAgKFNBVklfcG9zX3ZhbHVlID49IHBlcmNlbnRpbGVfMjBfU0FWSV9wb3NfdmFsdWUgJg0KICAgICAgIFNBVklfcG9zX3ZhbHVlIDw9IHBlcmNlbnRpbGVfODBfU0FWSV9wb3NfdmFsdWUpICYNCiAgICAgIChORFdJX21pbiA+PSBwZXJjZW50aWxlXzIwX05EV0lfbWluICYNCiAgICAgICAgIE5EV0lfbWluIDw9IHBlcmNlbnRpbGVfODBfTkRXSV9taW4pICYNCiAgICAgIChORE1JX21pbiA+PSBwZXJjZW50aWxlXzIwX05ETUlfbWluICYNCiAgICAgICAgIE5ETUlfbWluIDw9IHBlcmNlbnRpbGVfODBfTkRNSV9taW4pICYNCiAgICAgIChORE1JX21heCA+PSBwZXJjZW50aWxlXzIwX05ETUlfbWF4ICYNCiAgICAgICAgIE5ETUlfbWF4IDw9IHBlcmNlbnRpbGVfODBfTkRNSV9tYXgpDQogICAgKQ0KYGBgDQoNClNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXRzLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnRyYWluX2luZGljZXM3IDwtIHNhbXBsZSgxOm5yb3coZmlsdGVyZWRfZGF0YTcpLCAwLjcgKiBucm93KGZpbHRlcmVkX2RhdGE3KSkNCnRyYWluX2RhdGE3IDwtIGZpbHRlcmVkX2RhdGE3W3RyYWluX2luZGljZXM3LCBdDQp0ZXN0X2RhdGE3IDwtIGZpbHRlcmVkX2RhdGE3Wy10cmFpbl9pbmRpY2VzNywgXQ0KYGBgDQoNCk51bWJlciBvZiBwb2ludHMgcGVyIGNhdGVnb3J5IGZvciBmaWx0ZXJlZCBkYXRhOg0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGE3ICU+JSBjb3VudChFVU5JU2FfMSkNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KcmY3X1MyIDwtIHJ1bl9yZih2YXJzX1JGLCB0cmFpbl9kYXRhNywgIkVVTklTYV8xIikNCnByaW50KHJmN19TMiR0aW1lKQ0Kc2F2ZShyZjdfUzIsIGZpbGUgPSAib2JqZWN0cy8xMC9yZjdfUzIuUmRhdGEiKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpwcmVkaWN0aW9uc19yZjdfUzIgPC0gcHJlZGljdChyZjdfUzIkbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGE3LE9PQiA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCnNhdmUocHJlZGljdGlvbnNfcmY3X1MyLCBmaWxlID0gIm9iamVjdHMvMTAvcHJlZGljdGlvbnNfcmY3X1MyLlJkYXRhIikNCmBgYA0KDQpDb25mdXNpb24gbWF0cml4Og0KDQpgYGB7cn0NCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uc19yZjdfUzIsIHRlc3RfZGF0YTckRVVOSVNhXzEpDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCnZhcmltcF9yZjdfUzIgPC0gY29tcHV0ZV92YXJpbXAocmY3X1MyJG1vZGVsLCBucGVybSA9IDEwMCkNCnNhdmUodmFyaW1wX3JmN19TMiwgZmlsZSA9ICJvYmplY3RzLzEwL3ZhcmltcF9yZjdfUzIuUmRhdGEiKQ0KYGBgDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQp2YXJpbXBfcmY3X2NvbmRfUzIgPC0gY29tcHV0ZV92YXJpbXBfY29uZChyZjdfUzIkbW9kZWwsIG5wZXJtID0gMTAwKQ0KcHJpbnQodmFyaW1wX3JmN19jb25kX1MyJHRpbWUpDQpzYXZlKHZhcmltcF9yZjdfY29uZF9TMiwgZmlsZSA9ICJvYmplY3RzLzEwL3ZhcmltcF9yZjdfY29uZF9TMi5SZGF0YSIpDQpgYGANCg0KVmFyaWFibGUgSW1wb3J0YW5jZSBQbG90DQoNCmBgYHtyfQ0KcGxvdCh2YXJpbXBfcmY3X1MyJHZhcmltcCwgbWFyZ2luID0gYyg5LCAzLCAyLCAyKSkNCnBsb3QodmFyaW1wX3JmN19jb25kX1MyJHZhcmltcCwgbWFyZ2luID0gYyg5LCAzLCAyLCAyKSkNCmBgYA0KDQpST0MgY3VydmVzOg0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kcm9jX2RhdGE3X1MyIDwtIGNvbXB1dGVfcm9jX2xldmVsMShyZjdfUzIkbW9kZWwsIHRlc3RfZGF0YTcpDQpzYXZlKHJvY19kYXRhN19TMiwgZmlsZSA9ICJvYmplY3RzLzEwL3JvY19kYXRhN19TMi5SZGF0YSIpDQpgYGANCg0KYGBge3J9DQpyb2M3IDwtIGdncGxvdChyb2NfZGF0YTdfUzIkcm9jLCBhZXMoeCA9IEZQUiwgeSA9IFRQUiwgY29sb3IgPSBDbGFzcykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsNCiAgZ2VvbV9hYmxpbmUobGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNdWx0aWNsYXNzIFJPQyBDdXJ2ZXMgd2l0aCBBVUMiLA0KICAgIHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsDQogICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiLA0KICAgIGNvbG9yID0gIkNsYXNzIChBVUMpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpyb2M2DQpgYGANCg0KIyMjIE9MRDogUGxvdHMgcm91Z2ggdmFsaWRhdGlvbg0KDQpgYGB7cn0NCmdncGxvdChkYXRhX3ZhbGlkYXRpb24lPiUNCiAgICAgICAgIG11dGF0ZShydWxlc19icm9rZW4gPSBjYXNlX3doZW4oDQogICAgICAgICAgIHZhbGlkXzFfY291bnQgPT0gMSAmIHZhbGlkXzFfTkRXSSA9PSAid3JvbmciIH4gIk5EV0kiLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDEgJiB2YWxpZF8xX0NIID09ICJ3cm9uZyIgfiAiQ0giLA0KICAgICAgICAgICB2YWxpZF8xX2NvdW50ID09IDIgJg0KICAgICAgICAgICAgIHZhbGlkXzFfTkRXSSA9PSAid3JvbmciICYgdmFsaWRfMV9DSCA9PSAid3JvbmcifiAiTkRXSSArIENIIiwNCiAgICAgICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICAgICAgICkpLCANCiAgICAgICBhZXMoeCA9IHZhbGlkXzFfY291bnQsIGZpbGwgPSBydWxlc19icm9rZW4pKSArDQogIGdlb21fYmFyKCkgKyBsYWJzKHggPSAiTnVtYmVyIG9mIGJyb2tlbiBydWxlcyIpDQpgYGANCg0KUHJvcG9ydGlvbiBvZiBvYnNlcnZhdGlvbnMgbm90IHZhbGlkYXRlZCAoc28gZmFyKToNCg0KYGBge3J9DQpucm93KGRhdGFfdmFsaWRhdGlvbiAlPiUgZHBseXI6OmZpbHRlcih2YWxpZF8xX2NvdW50ID4gMCkpLw0KICBucm93KGRhdGFfdmFsaWRhdGlvbikNCmBgYA0KDQpCdXQgYmUgYXdhcmUgdGhhdCB0aGVyZSBhcmUgc3RpbGwgTUFOWSBtaXNzaW5nIFJTIGRhdGEuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgICAgICAgIG11dGF0ZShkaWZmX0dQUyA9IGlmX2Vsc2UoDQogICAgICAgICAgIExjdG5tdGggIT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgfA0KICAgICAgICAgICAgIGlzLm5hKExjdG5tdGgpLCAibm8iLCAieWVzIikpLCANCiAgICAgICBhZXMoeCA9IGRpZmZfR1BTLCBmaWxsID0gdmFsaWRfMSkpICsNCiAgZ2VvbV9iYXIoKSArIGxhYnMoeCA9ICJEaWZmZXJlbnRpYWwgR1BTIikNCmdncGxvdChkYXRhX3ZhbGlkYXRpb24gJT4lDQogICAgICAgICBtdXRhdGUoR1BTID0gY2FzZV93aGVuKA0KICAgICAgICAgICBMY3RubXRoID09ICJMb2NhdGlvbiB3aXRoIGRpZmZlcmVudGlhbCBHUFMiIH4gInllcyIsDQogICAgICAgICAgIExjdG5tdGggPT0gIkxvY2F0aW9uIHdpdGggR1BTIiB+ICJ5ZXMiLA0KICAgICAgICAgICBpcy5uYShMY3RubXRoKSB+ICJubyIsDQogICAgICAgICAgIFRSVUUgfiAibm8iDQogICAgICAgICApKSwgDQogICAgICAgYWVzKHggPSBHUFMsIGZpbGwgPSB2YWxpZF8xKSkgKw0KICBnZW9tX2JhcigpICsgbGFicyh4ID0gIkdQUyIpDQpgYGANCg0KIyBPTEQgZnJvbSBoZXJlDQoNCiMjIE1hcHMNCg0KIyMjIFBvaW50cyBHUFMNCg0KYGBge3J9DQojIExvYWQgd29ybGQgYm91bmRhcmllcw0Kd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikNCg0KIyBDYWxjdWxhdGUgdGhlIGV4dGVudCBvZiB0aGUgcG9pbnRzDQpwb2ludHNfR1BTX2V4dGVudCA8LSBkYXRhX3ZhbGlkYXRpb24gJT4lDQogIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PSBUICkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoYExvY2F0aW9uIG1ldGhvZGAgPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgfA0KICAgICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCA9PSAiTG9jYXRpb24gd2l0aCBHUFMiKSAlPiUNCiAgc3VtbWFyaXNlKGxvbl9taW4gPSBtaW4oTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBsb25fbWF4ID0gbWF4KExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIGxhdF9tYXggPSBtYXgoTGF0X3VwZGF0ZWQsIG5hLnJtID0gVFJVRSkpDQoNCiMgQWRkIHBhZGRpbmcgdG8gdGhlIGV4dGVudCAoYWRqdXN0IGFzIG5lZWRlZCkNCnBhZGRpbmcgPC0gMiAgIyBBZGp1c3QgcGFkZGluZyB0byB5b3VyIHByZWZlcmVuY2UNCnhfbGltaXRzIDwtIGMocG9pbnRzX0dQU19leHRlbnQkbG9uX21pbiAtIHBhZGRpbmcsDQogICAgICAgICAgICAgIHBvaW50c19HUFNfZXh0ZW50JGxvbl9tYXggKyBwYWRkaW5nKQ0KeV9saW1pdHMgPC0gYyhwb2ludHNfR1BTX2V4dGVudCRsYXRfbWluIC0gcGFkZGluZywNCiAgICAgICAgICAgICAgcG9pbnRzX0dQU19leHRlbnQkbGF0X21heCArIHBhZGRpbmcpDQoNCiMgQ3JlYXRlIHRoZSB6b29tZWQgbWFwDQpnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IHdvcmxkLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImdyYXkiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihTMl9kYXRhID09IFQgfCBMYW5kc2F0X2RhdGEgPT0gVCApICU+JQ0KICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihgTG9jYXRpb24gbWV0aG9kYCA9PSAiTG9jYXRpb24gd2l0aCBkaWZmZXJlbnRpYWwgR1BTIiB8DQogICAgICAgICAgIGBMb2NhdGlvbiBtZXRob2RgID09ICJMb2NhdGlvbiB3aXRoIEdQUyIpLA0KICAgICAgICAgICAgIGFlcyh4ID0gTG9uX3VwZGF0ZWQsIHkgPSBMYXRfdXBkYXRlZCwgY29sb3IgPSBFVU5JU2FfMSksDQogICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgY29vcmRfc2YoeGxpbSA9IHhfbGltaXRzLCB5bGltID0geV9saW1pdHMpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KTnVtYmVyIG9mIEdQUyBwb2ludHMgYnkgQ291bnRyeToNCg0KYGBge3J9DQpkYXRhX3ZhbGlkYXRpb24gJT4lDQogIGRwbHlyOjpmaWx0ZXIoRVVOSVNhXzEgJWluJSBjKCJUIiwgIlIiLCAiUyIsICJRIikpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PSBUICkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoYExvY2F0aW9uIG1ldGhvZGAgPT0gIkxvY2F0aW9uIHdpdGggZGlmZmVyZW50aWFsIEdQUyIgfA0KICAgICAgICAgICBgTG9jYXRpb24gbWV0aG9kYCA9PSAiTG9jYXRpb24gd2l0aCBHUFMiKSAlPiUNCiAgY291bnQoQ291bnRyeSkNCmBgYA0KDQojIyMgUG9pbnRzIFJlU3VydmV5DQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIGV4dGVudCBvZiB0aGUgcG9pbnRzDQpwb2ludHNfcmVzdXJ2ZXlfZXh0ZW50IDwtIGRhdGFfdmFsaWRhdGlvbiAlPiUNCiAgZHBseXI6OmZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogIGRwbHlyOjpmaWx0ZXIoUzJfZGF0YSA9PSBUIHwgTGFuZHNhdF9kYXRhID09IFQgKSAlPiUNCiAgc3VtbWFyaXNlKGxvbl9taW4gPSBtaW4oTG9uX3VwZGF0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBsb25fbWF4ID0gbWF4KExvbl91cGRhdGVkLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbGF0X21pbiA9IG1pbihMYXRfdXBkYXRlZCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIGxhdF9tYXggPSBtYXgoTGF0X3VwZGF0ZWQsIG5hLnJtID0gVFJVRSkpDQoNCiMgQWRkIHBhZGRpbmcgdG8gdGhlIGV4dGVudCAoYWRqdXN0IGFzIG5lZWRlZCkNCnBhZGRpbmcgPC0gMiAgIyBBZGp1c3QgcGFkZGluZyB0byB5b3VyIHByZWZlcmVuY2UNCnhfbGltaXRzIDwtIGMocG9pbnRzX3Jlc3VydmV5X2V4dGVudCRsb25fbWluIC0gcGFkZGluZywNCiAgICAgICAgICAgICAgcG9pbnRzX3Jlc3VydmV5X2V4dGVudCRsb25fbWF4ICsgcGFkZGluZykNCnlfbGltaXRzIDwtIGMocG9pbnRzX3Jlc3VydmV5X2V4dGVudCRsYXRfbWluIC0gcGFkZGluZywNCiAgICAgICAgICAgICAgcG9pbnRzX3Jlc3VydmV5X2V4dGVudCRsYXRfbWF4ICsgcGFkZGluZykNCg0KIyBDcmVhdGUgdGhlIHpvb21lZCBtYXANCmdncGxvdCgpICsNCiAgZ2VvbV9zZihkYXRhID0gd29ybGQsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiZ3JheSIpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV92YWxpZGF0aW9uICU+JQ0KICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihFVU5JU2FfMSAlaW4lIGMoIlQiLCAiUiIsICJTIiwgIlEiKSkgJT4lDQogICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKFMyX2RhdGEgPT0gVCB8IExhbmRzYXRfZGF0YSA9PSBUICksDQogICAgICAgICAgICAgYWVzKHggPSBMb25fdXBkYXRlZCwgeSA9IExhdF91cGRhdGVkLCBjb2xvciA9IEVVTklTYV8xKSwNCiAgICAgICAgICAgICBzaXplID0gMSkgKw0KICBjb29yZF9zZih4bGltID0geF9saW1pdHMsIHlsaW0gPSB5X2xpbWl0cykgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpOdW1iZXIgb2YgUmVTdXJ2ZXkgcG9pbnRzIGJ5IENvdW50cnk6DQoNCmBgYHtyfQ0KZGF0YV92YWxpZGF0aW9uICU+JQ0KICBkcGx5cjo6ZmlsdGVyKEVVTklTYV8xICVpbiUgYygiVCIsICJSIiwgIlMiLCAiUSIpKSAlPiUNCiAgZHBseXI6OmZpbHRlcihTMl9kYXRhID09IFQgfCBMYW5kc2F0X2RhdGEgPT0gVCApICU+JQ0KICBjb3VudChDb3VudHJ5KQ0KYGBgDQoNCiMjIENvcmRpbGxlcmEgZGF0YQ0KDQpgYGB7cn0NCkFscGluZUdyYXNzbGFuZHNfaW5kaWNlcyA8LSByZWFkX2NzdigNCiAgIkM6L0RhdGEvTU9USVZBVEUvQ29yZGlsbGVyYS9BbHBpbmVHcmFzc2xhbmRzL0FscGluZUdyYXNzbGFuZF9TZW50aW5lbF9QbG90X0FsbHllYXJfQWxsbWV0cmljcy5jc3YiKQ0KQWxwaW5lR3Jhc3NsYW5kc19waGVuIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL0FscGluZUdyYXNzbGFuZHMvQWxwaW5lR3Jhc3NsYW5kc19QaGVub2xvZ3lfU09TX0VPU19QZWFrX05EVklfQW1wbGl0dWRlLmNzdiIpDQpBbHBpbmVHcmFzc2xhbmRzX0NIIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL0FscGluZUdyYXNzbGFuZHMvQWxwaW5lR3Jhc3NsYW5kc19DYW5vcHlIZWlnaHRfMW0uY3N2IikNClZlZ2V0YXRpb25UeXBlc19pbmRpY2VzIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL1ZlZ2V0YXRpb25UeXBlcy9WZWdldGF0aW9uVHlwZXNfU2VudGluZWxfUGxvdF9BbGxZZWFyX0FsbG1ldHJpY3MuY3N2IikNClZlZ2V0YXRpb25UeXBlc19waGVuIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL1ZlZ2V0YXRpb25UeXBlcy9WZWdldGF0aW9uVHlwZXNfUGhlbm9sb2d5X1NPU19FT1NfUGVha19ORFZJX0FtcGxpdHVkZS5jc3YiKQ0KVmVnZXRhdGlvblR5cGVzX0NIIDwtIHJlYWRfY3N2KA0KICAiQzovRGF0YS9NT1RJVkFURS9Db3JkaWxsZXJhL1ZlZ2V0YXRpb25UeXBlcy9WZWdldGF0aW9uVHlwZXNfQ2Fub3B5SGVpZ2h0XzFtLmNzdiIpDQpgYGANCg0KYGBge3J9DQpBbHBpbmVHcmFzc2xhbmRzIDwtIEFscGluZUdyYXNzbGFuZHNfaW5kaWNlcyAlPiUNCiAgc2VsZWN0KC1gc3lzdGVtOmluZGV4YCwgLS5nZW8sIC1Mb2NhbGlkYWQpICU+JQ0KICByZW5hbWUoSMOhYml0YXQgPSAiSO+/vWJpdGF0IikgJT4lIA0KICBmdWxsX2pvaW4oQWxwaW5lR3Jhc3NsYW5kc19waGVuICAlPiUNCiAgICAgICAgICAgICAgc2VsZWN0KC1gc3lzdGVtOmluZGV4YCwgLS5nZW8sIC1Mb2NhbGlkYWQpICU+JQ0KICAgICAgICAgICAgICByZW5hbWUoSMOhYml0YXQgPSAiSO+/vWJpdGF0IikpICU+JQ0KICBmdWxsX2pvaW4oQWxwaW5lR3Jhc3NsYW5kc19DSCAgJT4lDQogICAgICAgICAgICAgIHNlbGVjdCgtYHN5c3RlbTppbmRleGAsIC0uZ2VvLCAtTG9jYWxpZGFkKSkgJT4lDQogIHNlbGVjdCgtRGF0ZV9feWVhciwgLSBgUHJlY2lzae+/vW5gKSAlPiUNCiAgbXV0YXRlKERBVEUgPSB5bWQoREFURSkpICU+JQ0KICByZW5hbWUoSUQgPSAiUmVsZXZlX251bSIpICU+JQ0KICBtdXRhdGUoSUQgPSBhcy5jaGFyYWN0ZXIoSUQpKSAlPiUNCiAgbXV0YXRlKGxheWVyID0gIkFscGluZUdyYXNzbGFuZHMiKQ0KYGBgDQoNCmBgYHtyfQ0KVmVnZXRhdGlvblR5cGVzIDwtIFZlZ2V0YXRpb25UeXBlc19pbmRpY2VzICU+JQ0KICBzZWxlY3QoLWBzeXN0ZW06aW5kZXhgLCAtLmdlbykgJT4lDQogIGZ1bGxfam9pbihWZWdldGF0aW9uVHlwZXNfcGhlbiAgJT4lDQogICAgICAgICAgICAgIHNlbGVjdCgtYHN5c3RlbTppbmRleGAsIC0uZ2VvKSkgJT4lDQogIGZ1bGxfam9pbihWZWdldGF0aW9uVHlwZXNfQ0ggICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoLWBzeXN0ZW06aW5kZXhgLCAtLmdlbykpICU+JQ0KICByZW5hbWUoSMOhYml0YXQgPSAiVFlQRSIpICU+JQ0KICBtdXRhdGUobGF5ZXIgPSAiVmVnZXRhdGlvblR5cGVzIikNCmBgYA0KDQpNZXJnZSBib3RoIGRhdGFzZXRzOg0KDQpgYGB7cn0NCmNvcmRpbGxlcmEgPC0gYmluZF9yb3dzKA0KICBBbHBpbmVHcmFzc2xhbmRzICU+JSBzZWxlY3QoREFURSwgSUQsIHN0YXJ0c193aXRoKCJORE1JIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aCgiTkRWSSIpLCBIw6FiaXRhdCwgIkVPU19ET1kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBlYWtfRE9ZIiwgIlNPU19ET1kiLCAiU2Vhc29uX0xlbmd0aCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2Fub3B5X2hlaWdodCIsICJsYXllciIpLA0KICBWZWdldGF0aW9uVHlwZXMgJT4lIHNlbGVjdChEQVRFLCBJRCwgc3RhcnRzX3dpdGgoIk5ETUkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJORFZJIiksIEjDoWJpdGF0LCAiRU9TX0RPWSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGVha19ET1kiLCAiU09TX0RPWSIsICJTZWFzb25fTGVuZ3RoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYW5vcHlfaGVpZ2h0IiwgImxheWVyIikNCiAgKSAlPiUNCiAgbXV0YXRlKEVVTklTYV8xID0gY2FzZV93aGVuKA0KICAgIEjDoWJpdGF0ID0gc3RyX2RldGVjdChIw6FiaXRhdCwgIlBhc3RpemFsfENlcnZ1bmFsfGdyYXNzbGFuZHxtZWFkb3ciKSB+ICJSIiwNCiAgICBIw6FiaXRhdCA9IHN0cl9kZXRlY3QoSMOhYml0YXQsICJmb3Jlc3QiKSB+ICJUIiwNCiAgICBIw6FiaXRhdCA9IHN0cl9kZXRlY3QoSMOhYml0YXQsICJTY3J1YnxzY3J1YnxTaHJ1YmxhbmR8c2hydWJsYW5kfHNocnVifEhlYXRobGFuZCIpIH4gIlMiLA0KICAgIEjDoWJpdGF0ID0gc3RyX2RldGVjdChIw6FiaXRhdCwgIlN1ZWxvfFNjcmVlfHNjcmVlfGNsaWZmIikgfiAiVSIsDQogICAgSMOhYml0YXQgPSBpcy5uYShIw6FiaXRhdCkgfiAiUiIsDQogICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8pLA0KICAgIEVVTklTYV8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNhXzEgPT0gIlIiIH4gIkdyYXNzbGFuZHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTYV8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTYV8xID09ICJVIiB+ICJJbmxhbmQgaGFiaXRhdHMgd2l0aCBubyBvciBsaXR0bGUgc29pbCIpDQogICAgKQ0KYGBgDQoNCiMjIyBORFZJLCBORE1JDQoNCmBgYHtyfQ0KZGlzdHJfcGxvdChjb3JkaWxsZXJhLA0KICAgICAgICAgICBjKCJORFZJX21heCIsICJORFZJX3A5MCIsICJORFZJX21pbiIsICJORFZJX3AxMCIpLCANCiAgICAgICAgICAgYygiTkRWSSBtYXgiLCAiTkRWSSBwOTAiLCAiTkRWSSBtaW4iLCAiTkRWSSBwMTAiKSkNCmRpc3RyX3Bsb3QoY29yZGlsbGVyYSwNCiAgICAgICAgICAgYygiTkRNSV9tYXgiLCAiTkRNSV9wOTAiLCAiTkRNSV9taW4iLCAiTkRNSV9wMTAiKSwgDQogICAgICAgICAgIGMoIk5ETUkgbWF4IiwgIk5ETUkgcDkwIiwgIk5ETUkgbWluIiwgIk5ETUkgcDEwIikpDQpgYGANCg0KIyBTZXNzaW9uIGluZm8NCg0KYGBge3J9DQpzZXNzaW9uSW5mbygpDQpgYGANCg0K